extensions: file_storage: directory: /var/lib/han-otel/host-collector timeout: 10s receivers: filelog/docker: include: [/var/lib/docker/containers/*/*-json.log] start_at: end include_file_path: true storage: file_storage operators: - type: json_parser id: docker-json timestamp: parse_from: attributes.time layout_type: gotime layout: "2006-01-02T15:04:05.000000000Z07:00" - type: regex_parser id: docker-service-label parse_from: attributes["attrs"]["tag"] if: 'attributes["attrs"]["tag"] != nil' regex: '^(?Pnginx|keycloak|redis)$' - type: move from: attributes.docker_service to: resource["service.name"] - type: move from: attributes.stream to: attributes["log.iostream"] - type: move from: attributes.log to: body - type: json_parser id: structured-body parse_from: body parse_to: attributes if: 'body matches "^\\s*\\{"' on_error: send journald/host: directory: /var/log/journal units: - docker.service - han-stack@production.service - han-secrets@production.service - han-chat-docker-firewall.service - certbot.service - fail2ban.service priority: info start_at: end storage: file_storage processors: memory_limiter: check_interval: 1s limit_mib: 192 spike_limit_mib: 48 resource/common: attributes: - {key: service.namespace, value: han-chat, action: upsert} - {key: deployment.environment, value: "${env:APP_ENV}", action: upsert} - {key: service.version, value: "${env:RELEASE_VERSION}", action: upsert} - {key: host.name, value: "${env:HOST_NAME}", action: upsert} transform/journal: error_mode: ignore log_statements: - context: log statements: - set(resource.attributes["service.name"], attributes["_SYSTEMD_UNIT"]) where resource.attributes["service.name"] == nil and attributes["_SYSTEMD_UNIT"] != nil - set(attributes["event.source"], "journald") where attributes["_SYSTEMD_UNIT"] != nil - set(attributes["event.source"], "docker-json-file") where attributes["_SYSTEMD_UNIT"] == nil - set(attributes["route.class"], "api") where resource.attributes["service.name"] == "nginx" and IsMatch(attributes["uri"], "^/api/") - set(attributes["route.class"], "auth") where resource.attributes["service.name"] == "nginx" and IsMatch(attributes["uri"], "^/auth/") - set(attributes["route.class"], "bitrix") where resource.attributes["service.name"] == "nginx" and IsMatch(attributes["uri"], "^/bitrix/") - set(attributes["route.class"], "callbacks") where resource.attributes["service.name"] == "nginx" and IsMatch(attributes["uri"], "^/callbacks/") - set(attributes["route.class"], "static") where resource.attributes["service.name"] == "nginx" and attributes["route.class"] == nil - replace_pattern(body, "(?i)(Bearer|Basic)\\s+[A-Za-z0-9._~+/=-]+", "$$1 [REDACTED]") where IsString(body) - replace_pattern(body, "(?i)(token|secret|password|code)=([^&\\s]+)", "$$1=[REDACTED]") where IsString(body) - set(body, "nginx.access") where resource.attributes["service.name"] == "nginx" and attributes["log.iostream"] == "stdout" - set(body, "keycloak.event") where resource.attributes["service.name"] == "keycloak" and attributes["log.iostream"] == "stdout" attributes/redact: actions: - {key: authorization, action: delete} - {key: http.request.header.authorization, action: delete} - {key: http.request.header.cookie, action: delete} - {key: http.response.header.set-cookie, action: delete} - {key: url.query, action: delete} - {key: url.full, action: delete} - {key: http.target, action: delete} - {key: http.request.body, action: delete} - {key: db.statement, action: delete} - {key: message, action: delete} - {key: payload, action: delete} - {key: user.phone, action: delete} - {key: user.email, action: delete} - {key: remote_addr, action: delete} - {key: user_agent, action: delete} - {key: uri, action: delete} filter/allowlist: error_mode: ignore logs: log_record: - 'attributes["_SYSTEMD_UNIT"] == nil and resource.attributes["service.name"] != "nginx" and resource.attributes["service.name"] != "keycloak" and resource.attributes["service.name"] != "redis"' - 'resource.attributes["service.name"] == "otel-collector" or resource.attributes["service.name"] == "otel-host-collector"' filter/noise: error_mode: ignore logs: log_record: - 'resource.attributes["service.name"] == "nginx" and severity_number < SEVERITY_NUMBER_WARN and IsMatch(body, "(/health/live|/nginx-health/live|/.well-known/acme-challenge/)")' batch: timeout: 5s send_batch_size: 512 send_batch_max_size: 1024 exporters: otlp/remote: endpoint: "${env:OTEL_REMOTE_ENDPOINT}" headers: authorization: ${file:/run/han-chat/secrets/OTEL_REMOTE_AUTH_HEADER} tls: insecure: "${env:OTEL_REMOTE_TLS_INSECURE}" sending_queue: enabled: true queue_size: 5000 storage: file_storage retry_on_failure: enabled: true initial_interval: 5s max_interval: 30s max_elapsed_time: 0s service: extensions: [file_storage] pipelines: logs: receivers: [filelog/docker, journald/host] processors: [memory_limiter, resource/common, filter/allowlist, filter/noise, transform/journal, attributes/redact, batch] exporters: [otlp/remote] telemetry: logs: level: error metrics: level: none