[Prod] Add Datadog

Let's see how this one works out. Cute logo
This commit is contained in:
Earlopain 2024-04-09 20:16:29 +02:00
parent 40ff649250
commit 0b47770c49
No known key found for this signature in database
GPG Key ID: 48860312319ADF61
12 changed files with 97 additions and 13 deletions

View File

@ -50,7 +50,9 @@
# The application must have its OAuth2 redirect URI set to ${JOINER_BASE_URL}/callback. # The application must have its OAuth2 redirect URI set to ${JOINER_BASE_URL}/callback.
# You also need to fill out all the JOINER_* environment variables below. # You also need to fill out all the JOINER_* environment variables below.
# #
# COMPOSE_PROFILES=discord # datadog: Start the datadog agent to push performance metrics through ddtrace.
# You also need to fill out the DD_API_KEY environment variables below.
# COMPOSE_PROFILES=discord,datadog
# Change the ports that are forwarded by docker to avoid potential conflicts # Change the ports that are forwarded by docker to avoid potential conflicts
@ -64,3 +66,13 @@
# JOINER_OAUTH2_CLIENT_SECRET= # JOINER_OAUTH2_CLIENT_SECRET=
# JOINER_GUILD_ID= # JOINER_GUILD_ID=
# JOINER_FAILED_JOIN_WEBHOOK_URL= # JOINER_FAILED_JOIN_WEBHOOK_URL=
# The following environment variables are used when using the 'datadog' profile:
# Required:
# DD_API_KEY=
#
# Optional:
# DD_SITE=us5.datadoghq.com
# DD_SERVICE=E6ng (Dev)
# DD_ENV=local

View File

@ -35,6 +35,8 @@ gem "rugged"
# Blocked by unicorn which lacks a release with Rack 3 support # Blocked by unicorn which lacks a release with Rack 3 support
gem "rack", "~> 2.0" gem "rack", "~> 2.0"
gem "datadog", require: "datadog/auto_instrument"
gem 'opensearch-ruby' gem 'opensearch-ruby'
gem 'mailgun-ruby' gem 'mailgun-ruby'

View File

@ -115,7 +115,14 @@ GEM
rexml rexml
crass (1.0.6) crass (1.0.6)
dalli (3.2.6) dalli (3.2.6)
datadog (2.0.0.beta1)
base64
debase-ruby_core_source (= 3.3.1)
libdatadog (~> 6.0.0.2.0)
libddwaf (~> 1.14.0.0.0)
msgpack
date (3.3.4) date (3.3.4)
debase-ruby_core_source (3.3.1)
debug (1.9.1) debug (1.9.1)
irb (~> 1.10) irb (~> 1.10)
reline (>= 0.3.8) reline (>= 0.3.8)
@ -163,6 +170,9 @@ GEM
jsonapi-renderer (0.2.2) jsonapi-renderer (0.2.2)
kgio (2.11.4) kgio (2.11.4)
language_server-protocol (3.17.0.3) language_server-protocol (3.17.0.3)
libdatadog (6.0.0.2.0)
libddwaf (1.14.0.0.0)
ffi (~> 1.0)
listen (3.8.0) listen (3.8.0)
rb-fsevent (~> 0.10, >= 0.10.3) rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10) rb-inotify (~> 0.9, >= 0.9.10)
@ -380,6 +390,7 @@ DEPENDENCIES
bcrypt bcrypt
bootsnap bootsnap
dalli dalli
datadog
debug debug
diffy diffy
dotenv dotenv

View File

@ -1,20 +1,22 @@
# frozen_string_literal: true # frozen_string_literal: true
class DanbooruLogger class DanbooruLogger
def self.log(exception, expected: false, **_params) def self.log(exception, expected: false)
if expected if expected
Rails.logger.info("#{exception.class}: #{exception.message}") Rails.logger.info("#{exception.class}: #{exception.message}")
else else
backtrace = Rails.backtrace_cleaner.clean(exception.backtrace).join("\n") backtrace = Rails.backtrace_cleaner.clean(exception.backtrace).join("\n")
Rails.logger.error("#{exception.class}: #{exception.message}\n#{backtrace}") Rails.logger.error("#{exception.class}: #{exception.message}\n#{backtrace}")
end end
Datadog::Tracing.active_span&.set_error(exception) unless expected
end end
def self.initialize(user) def self.initialize(user)
add_attributes("user.id" => user.id, "user.name" => user.name) add_attributes("user.id" => user.id) unless user.is_anonymous?
end end
def self.add_attributes(**) def self.add_attributes(**)
# noop Datadog::Tracing.active_span&.set_tags(**)
end end
end end

View File

@ -217,8 +217,7 @@ class ApplicationRecord < ActiveRecord::Base
def with_timeout(n, default_value = nil) def with_timeout(n, default_value = nil)
connection.execute("SET STATEMENT_TIMEOUT = #{n}") unless Rails.env == "test" connection.execute("SET STATEMENT_TIMEOUT = #{n}") unless Rails.env == "test"
yield yield
rescue ::ActiveRecord::StatementInvalid => x rescue ::ActiveRecord::StatementInvalid
DanbooruLogger.log(x, expected: true)
return default_value return default_value
ensure ensure
connection.execute("SET STATEMENT_TIMEOUT = #{CurrentUser.user.try(:statement_timeout) || 3_000}") unless Rails.env == "test" connection.execute("SET STATEMENT_TIMEOUT = #{CurrentUser.user.try(:statement_timeout) || 3_000}") unless Rails.env == "test"

View File

@ -206,8 +206,6 @@ class TagAlias < TagRelationship
forum_updater.update(failure_message(e), "FAILED") if update_topic forum_updater.update(failure_message(e), "FAILED") if update_topic
update_columns(status: "error: #{e}") update_columns(status: "error: #{e}")
end end
DanbooruLogger.log(e, tag_alias_id: id, antecedent_name: antecedent_name, consequent_name: consequent_name)
end end
end end

View File

@ -138,8 +138,6 @@ class TagImplication < TagRelationship
forum_updater.update(failure_message(e), "FAILED") if update_topic forum_updater.update(failure_message(e), "FAILED") if update_topic
update_columns(status: "error: #{e}") update_columns(status: "error: #{e}")
DanbooruLogger.log(e, tag_implication_id: id, antecedent_name: antecedent_name, consequent_name: consequent_name)
end end
end end

View File

@ -0,0 +1,10 @@
# frozen_string_literal: true
module SensitiveParams
# Common values for Rails and Datadog query parameter filtering
PARAMS = %i[passw secret token _key crypt salt certificate otp ssn email].freeze
def self.to_datadog_regex
Regexp.new("(?:#{PARAMS.join('|')})[^&]*=[^&]*")
end
end

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
Datadog.configure do |c|
c.tracing.enabled = !Rails.env.test? && ENV["DD_API_KEY"].present?
c.logger.level = Logger::WARN
c.tracing.instrument :rack, quantize: {
query: {
obfuscate: {
regex: SensitiveParams.to_datadog_regex,
},
},
}
end

View File

@ -5,6 +5,4 @@
# Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file.
# Use this to limit dissemination of sensitive information. # Use this to limit dissemination of sensitive information.
# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.
Rails.application.config.filter_parameters += [ Rails.application.config.filter_parameters += SensitiveParams::PARAMS
:passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :email
]

View File

@ -15,6 +15,7 @@ x-environment: &common-env
SESSION_SECRET_KEY: 44b4f44e9f253c406cbe727d403d500c1cecff943e4d2aea8f5447f28846fffe SESSION_SECRET_KEY: 44b4f44e9f253c406cbe727d403d500c1cecff943e4d2aea8f5447f28846fffe
# Hide annoying output from libvips on corrupt files # Hide annoying output from libvips on corrupt files
VIPS_WARNING: "0" VIPS_WARNING: "0"
DD_TRACE_STARTUP_LOGS: false
x-depends-on: &common-depends-on x-depends-on: &common-depends-on
opensearch: opensearch:
@ -43,6 +44,9 @@ services:
environment: environment:
<<: *common-env <<: *common-env
RAILS_ENV: development RAILS_ENV: development
DD_AGENT_HOST: datadog-agent
DD_SERVICE: ${DD_SERVICE:-}
DD_ENV: ${DD_ENV:-}
depends_on: depends_on:
<<: *common-depends-on <<: *common-depends-on
autocompleted: autocompleted:
@ -118,6 +122,17 @@ services:
command: iqdb http 0.0.0.0 5588 /iqdb/e621_v2.db command: iqdb http 0.0.0.0 5588 /iqdb/e621_v2.db
volumes: volumes:
- iqdb_data:/iqdb - iqdb_data:/iqdb
datadog-agent:
image: datadog/agent:7.52.0
environment:
DD_API_KEY: ${DD_API_KEY:-}
DD_SITE: ${DD_SITE:-us5.datadoghq.com}
DD_HOSTNAME: datadog-agent
DD_LOG_LEVEL: WARN
DD_APM_NON_LOCAL_TRAFFIC: true
profiles:
- datadog
# Discord integration # Discord integration

25
test/unit/datadog_test.rb Normal file
View File

@ -0,0 +1,25 @@
# frozen_string_literal: true
require "test_helper"
class DatadogTest < ActiveSupport::TestCase
def assert_match_group(expected, query)
assert_equal(expected, query[SensitiveParams.to_datadog_regex])
end
def assert_no_match_group(query)
assert_nil(query[SensitiveParams.to_datadog_regex])
end
should "filters query parameters" do
assert_match_group("password=hunter2", "password=hunter2")
assert_match_group("password=hunter2", "?abc=def&password=hunter2&foo=bar")
# These partial matches are fine, just don't let datadog grab it
assert_match_group("password]=hunter2", "?abc=def&user[password]=hunter2&foo=bar")
assert_match_group("password=hunter2", "old_password=hunter2")
assert_match_group("password_old=hunter2", "password_old=hunter2")
assert_no_match_group("something=else")
assert_no_match_group("search[foo]=bar")
end
end