When I send an email with an attachment the data is logged in hex and fills up my whole log. Is there a way to disable logging of attachments?
I know I can disable mailer logging with config.action_mailer.logger = nil.
Unfortunately, the attachments are included in the logs if the logging level is set to :debug, the default level for non-production environments. This means that in production you should be fine, but your dev and staging environments could bloat during testing. You could turn down logging for your entire app (config.log_level = :info), but this is obviously less than ideal.
You can configure a custom logger:
config.action_mailer.logger = ActiveSupport::BufferedLogger.new("mailer.log")
config.action_mailer.logger.level = ActiveSupport::BufferedLogger::Severity::INFO
Rails 4
config.action_mailer.logger = ActiveSupport::Logger.new("mailer.log")
config.action_mailer.logger.level = ActiveSupport::Logger::Severity::INFO
This will split the log, but you can isolate the logging level change to the action mailer.
If you still want your log level to be debug, you can remove the attachments from the log output by overriding ActionMailer's LogSubscriber class. Look at the class in your actionmailer gem, and adjust accordingly. For my Rails 4.2.10 install, the relevant file is:
gems/actionmailer-4.2.10/lib/action_mailer/log_subscriber.rb
My module is:
module ActionMailer
class LogSubscriber < ActiveSupport::LogSubscriber
def deliver(event)
info do
recipients = Array(event.payload[:to]).join(', ')
"\nSent mail to #{recipients}, Subject: #{event.payload[:subject]}, on #{event.payload[:date]} (#{event.duration.round(1)}ms)"
end
debug { remove_attachments(event.payload[:mail]) }
end
def remove_attachments(message)
new_message = ''
skip = false
message.each_line do |line|
new_message << line unless skip
skip = true if line =~ /Content-Disposition: attachment;/
skip = false if skip && line =~ /----==_mimepart/
end
new_message
end
end
end
Save this to an .rb file anywhere under your app/ folder and it will be included.
in Application.rb you could try filtering the attachment parameter. I believe this should solve the issue, but I have not tested it myself
config.filter_parameters += [:attachment]
Related
Problem
I seem to be facing a stubborn issue with my RSpec tests trying to constantly send emails in test-env despite my configuration should avoid it. Whatever I try it seems to totally ignore it.
My environment
Rails 6.1.1
Ruby 3.0.0
sendgrid-ruby gem 6.3.9
I have a mailer class inheritance-chain as follows: OrganizationMailer<-ApplicationMailer<-ActionMailer::Base
In my config/environments/test.rb I have the following mail-related configuration
config.action_mailer.delivery_method = :test
config.action_mailer.perform_deliveries = false
config.active_job.queue_adapter = :test
My config/application.rb and config/environment.rb don't contain any extra configuration.
Bit offtopic maybe, but just in case adding it as well:
I have ensured that the line ENV['RAILS_ENV'] ||= 'test' is present in both- spec/spec_helper.rb and spec/rails_helper.rb since for some reason Rails randomly triggered my tests in staging environment. I also had to change the .rspec file contents in my project from --require rspec_helper to --require rails_helper. I got the solution from here1, here2 and here3. But this I don't think plays any role in current problem.
Bad solution
I hacked my way through this issue atm by just adding the unless Rails.env.test? on top of each email-sending method I'm having to ensure none of them reach Sendgrid in tests, but this sucks big time I know. That's why I'm posting this question to get this fixed properly without such if/unless-clauses.
My theory
Can it be that sendgrid-ruby is here to blame? I inherit things from ApplicationMailer and ActionMailer but eventually what is sending the emails is Sendgrid ruby gem. If so, how to avoid it best? I didn't find any hints from the sendgrid-ruby documentation about that. I will post one of my simple mailer-methods below so you could see the situation atm:
# frozen_string_literal: true
# using SendGrid's Ruby Library
# https://github.com/sendgrid/sendgrid-ruby
require 'sendgrid-ruby'
class MyMailer < ApplicationMailer
include SendGrid
def my_mailer_method(my_object:)
unless Rails.env.test? # <---- Hack I'd like to get rid of
from = Email.new(email: 'no-reply#my.domain', name: t('general.title'))
to = Email.new(email: my_object.contact_email)
subject = "#{t('my_mailer.my_mailer_method.subject')}: #{my_object.my_object_title}"
content = Content.new(
type: 'text/html',
value: ApplicationController.render(
template: 'my_mailer/my_mailer_method',
locals: {
my_object: my_object
},
layout: nil
)
)
mail = SendGrid::Mail.new(from, subject, to, content)
sg = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])
# Send out mail
response = sg.client.mail._('send').post(request_body: mail.to_json)
end
end
end
The issue here is that you may have set config.action_mailer.delivery_method = :test but you are not actually using ActionMailer to deliver your emails. Instead, within the MyMailer class you are directly using the SendGrid API, and sidestepping ActionMailer entirely.
If you want to use SendGrid API to send your emails, then I actually recommend using the sendgrid-actionmailer gem. It allows you to use ActionMailer to build your emails and uses the SendGrid API under the hood to send them. This allows you to send other parameters that the API supports and would be more difficult or impossible with SMTP, while still using the Rails standard ActionMailer to send the emails.
To ensure that your mails are sent by SendGrid in production, but not sent in test, you would set:
config.action_mailer.delivery_method = :sendgrid_actionmailer
in your production.rb environment. And set:
config.action_mailer.delivery_method = :test
config.action_mailer.perform_deliveries = false
as you already have in your test.rb environment.
I can't seem to find how to setup rails 6 in a way that the logger sends an e-mail for all errors and fatal errors; i found loads of topics on how to customize the log messages themselves (and how to log emails that the app sends), but is there a way to connect actionmailer to the logger?
Thanks!
I recommend using something like this gem https://github.com/smartinez87/exception_notification to log errors.
However, you could achieve what you're asking for with a custom logger
class MailerLogger < Logger
def add(severity, message = nil, progname = nil)
if severity == Logger::Severity::FATAL
# Send email here
end
super(severity, message, progname)
end
end
# config/application.rb or config/environments/production.rb
config.logger = MailerLogger.new(STDOUT)
I'm looking for a way to configure a Rails server log only if the client has contacted a specific hostname. e.g. I could make it so that http://public.example.com doesn't get logged, but http://debug.example.com (same underlying Rails app server) does get logged (or ideally gets logged in more detail than the regular host). It would help with production debugging.
You can use gem Lograge to customize your log. This gem will give you much more custom to your log. For example, in your case, I will do this
After install the gem. Create a file at config/initializers/lograge.rb
# config/initializers/lograge.rb
Rails.application.configure do
config.lograge.enabled = true
config.lograge.custom_options = lambda do |event|
# custom log on specific domain
if event.payload[:host] == "debug.example.com"
{:host => event.payload[:host]}
else
{}
end
end
end
And in your Application Controller
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
# This will add request's host to lograge so you can use it to filter log later
def append_info_to_payload(payload)
super
payload[:host] = request.host
end
end
Now you can customize your log base on domain, on how to customize it please read at: https://github.com/roidrage/lograge
Our Rails logs are filled up with status pings from our load balancer, and it would be great to filter those out.
We want all other endpoints to be logged, but we'd like to be able to turn off application logging for this controller alone.
The controller looks like this:
class V2::StatusController < ApplicationController
skip_before_filter :authenticate
def ping
head :ok, text: 'OK'
end
end
Edit: Our logging is set up to send to Loggly:
require 'syslogger'
config.logger = Syslogger.new("APP_NAME", Syslog::LOG_PID, Syslog::LOG_LOCAL7)
config.lograge.enabled = true
config.lograge.formatter = Lograge::Formatters::Json.new
Since you're using lograge, This is from lograge's README for how to ignore actions:
To further clean up your logging, you can also tell Lograge to skip log messages meeting given criteria. You can skip log messages generated from certain controller actions, or you can write a custom handler to skip messages based on data in the log event:
# config/environments/production.rb
MyApp::Application.configure do
config.lograge.enabled = true
config.lograge.ignore_actions = ['home#index', 'aController#anAction']
config.lograge.ignore_custom = lambda do |event|
# return true here if you want to ignore based on the event
end
end
So yours specifically would probably be
config.logger = Syslogger.new("APP_NAME", Syslog::LOG_PID, Syslog::LOG_LOCAL7)
config.lograge.enabled = true
config.lograge.formatter = Lograge::Formatters::Json.new
config.lograge.ignore_actions = ["v2/status#ping", ...]
I am trying to output to the production log some logging that's happening in a function to which delay is being called on via delayed_job.
Example:
My controller
def create_something
#user = User.find(1)
#user.delay.do_something_crazy
end
My Model
def do_something_crazy
# some code
Rails.logger.info "Doing something crazy right now!"
end
The logging is not being output into my production log. Without delay, it does but with it seems to not?
Add initializer file delayed_jobs_settings.rb to config/initializers (unless you already have something like this for Delayed Jobs settings) and add this code:
Delayed::Worker.logger = Logger.new(Rails.root.join('log', 'dj.log'))
And it will save it to log/dj.log.
Or just
Delayed::Worker.logger = Rails.logger
for logging to default Rails log.