Skip to main content

Monitoring

Health Monitoring

Health Check Endpoints

ruby


# config/routes.rb
Rails.application.routes.draw do
get '/health', to: 'health#index'
get '/health/database', to: 'health#database'
get '/health/redis', to: 'health#redis'
get '/health/storage', to: 'health#storage'
end
# app/controllers/health_controller.rb
class HealthController < ApplicationController
def index
render json: {
status: 'healthy',
timestamp: Time.current.iso8601,
version: OpenProject::VERSION,
uptime: uptime_seconds
}
end
def database
ActiveRecord::Base.connection.execute('SELECT 1')
render json: { status: 'healthy', service: 'database' }
rescue => e
render json: { status: 'unhealthy', service: 'database', error: e.message }, status: 503
end
def redis
Redis.current.ping
render json: { status: 'healthy', service: 'redis' }
rescue => e
render json: { status: 'unhealthy', service: 'redis', error: e.message }, status: 503
end
private
def uptime_seconds
Time.current.to_i - Rails.application.config.start_time.to_i
end
end

Performance Monitoring

Application Metrics

ruby


# config/initializers/metrics.rb
require 'prometheus_exporter/middleware'
Rails.application.middleware.unshift PrometheusExporter::Middleware
# Custom metrics
class OpenProjectMetrics
def self.record_work_package_created
PrometheusExporter::Client.default.send_json(
type: 'counter',
name: 'openproject_work_packages_created_total',
help: 'Total number of work packages created'
)
end
def self.record_login
PrometheusExporter::Client.default.send_json(
type: 'counter',
name: 'openproject_user_logins_total',
help: 'Total number of user logins'
)
end
end

Database Performance Monitoring

ruby


# config/initializers/database_monitoring.rb
ActiveSupport::Notifications.subscribe('sql.active_record') do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
if event.duration > 1000 # Log slow queries (> 1 second)
Rails.logger.warn "Slow query detected: #{event.duration}ms - #{event.payload[:sql]}"
end
end

Logging Configuration

Structured Logging

ruby


# config/environments/production.rb
Rails.application.configure do
config.logger = ActiveSupport::Logger.new(STDOUT)
config.log_formatter = proc do |severity, datetime, progname, msg|
{
timestamp: datetime.iso8601,
level: severity,
message: msg,
environment: Rails.env,
version: OpenProject::VERSION
}.to_json + "\n"
end
end

Log Rotation

bash



# /etc/logrotate.d/openproject
/var/log/openproject/*.log {
daily
rotate 30
compress
delaycompress
missingok
notifempty
create 644 openproject openproject
postrotate
systemctl reload openproject
endscript
}

Monitoring Tools Integration

Prometheus Configuration

yaml


# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'openproject'
static_configs:
- targets: ['localhost:3000']
metrics_path: '/metrics'
scrape_interval: 30s

Grafana Dashboard

json


{
"dashboard": {
"title": "OpenProject Monitoring",
"panels": [
{
"title": "HTTP Request Rate",
"type": "graph",
"targets": [
{
"expr": "rate(http_requests_total[5m])"
}
]
},
{
"title": "Database Connection Pool",
"type": "graph",
"targets": [
{
"expr": "rails_database_pool_size"
}
]
},
{
"title": "Memory Usage",
"type": "graph",
"targets": [
{
"expr": "process_resident_memory_bytes"
}
]
}
]
}
}

Alerting Rules

yaml


# alerts.yml
groups:
- name: openproject
rules:
- alert: OpenProjectDown
expr: up{job="openproject"} == 0
for: 1m
labels:
severity: critical
annotations:
summary: "OpenProject is down"
description: "OpenProject has been down for more than 1 minute"
- alert: HighMemoryUsage
expr: process_resident_memory_bytes > 2e9
for: 5m
labels:
severity: warning
annotations:
summary: "High memory usage detected"
description: "Memory usage is above 2GB"