| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- api:
- enabled: true
- address: 0.0.0.0:9001
- sources:
- docker_host:
- type: docker_logs
- exclude_containers:
- - briven-vector
- transforms:
- project_logs:
- type: remap
- inputs:
- - docker_host
- source: |-
- .project = "default"
- .event_message = del(.message)
- .appname = del(.container_name)
- del(.container_created_at)
- del(.container_id)
- del(.source_type)
- del(.stream)
- del(.label)
- del(.image)
- del(.host)
- del(.stream)
- router:
- type: route
- inputs:
- - project_logs
- route:
- kong: '.appname == "briven-kong" || .appname == "briven-envoy"'
- auth: '.appname == "briven-auth"'
- rest: '.appname == "briven-rest"'
- realtime: '.appname == "realtime-dev.briven-realtime"'
- storage: '.appname == "briven-storage"'
- functions: '.appname == "briven-edge-functions"'
- db: '.appname == "briven-db"'
- # Ignores non nginx errors since they are related with kong booting up
- kong_logs:
- type: remap
- inputs:
- - router.kong
- source: |-
- req, err = parse_nginx_log(.event_message, "combined")
- if err == null {
- .timestamp = req.timestamp
- .metadata.request.headers.referer = req.referer
- .metadata.request.headers.user_agent = req.agent
- .metadata.request.headers.cf_connecting_ip = req.client
- .metadata.response.status_code = req.status
- url, split_err = split(req.request, " ")
- if split_err == null {
- .metadata.request.method = url[0]
- .metadata.request.path = url[1]
- .metadata.request.protocol = url[2]
- }
- }
- if err != null {
- abort
- }
- # Ignores non nginx errors since they are related with kong booting up
- kong_err:
- type: remap
- inputs:
- - router.kong
- source: |-
- .metadata.request.method = "GET"
- .metadata.response.status_code = 200
- parsed, err = parse_nginx_log(.event_message, "error")
- if err == null {
- .timestamp = parsed.timestamp
- .severity = parsed.severity
- .metadata.request.host = parsed.host
- .metadata.request.headers.cf_connecting_ip = parsed.client
- url, err = split(parsed.request, " ")
- if err == null {
- .metadata.request.method = url[0]
- .metadata.request.path = url[1]
- .metadata.request.protocol = url[2]
- }
- }
- if err != null {
- abort
- }
- # Gotrue logs are structured json strings which frontend parses directly. But we keep metadata for consistency.
- auth_logs:
- type: remap
- inputs:
- - router.auth
- source: |-
- parsed, err = parse_json(.event_message)
- if err == null {
- .metadata.timestamp = parsed.time
- .metadata = merge!(.metadata, parsed)
- }
- # PostgREST logs are structured so we separate timestamp from message using regex
- rest_logs:
- type: remap
- inputs:
- - router.rest
- source: |-
- parsed, err = parse_regex(.event_message, r'^(?P<time>.*): (?P<msg>.*)$')
- if err == null {
- .event_message = parsed.msg
- .timestamp = parse_timestamp!(value: parsed.time,format: "%d/%b/%Y:%H:%M:%S %z")
- .metadata.host = .project
- }
- # Filter out healthcheck logs from Realtime
- realtime_logs_filtered:
- type: filter
- inputs:
- - router.realtime
- condition: '!contains(string!(.event_message), "/health")'
- # Realtime logs are structured so we parse the severity level using regex (ignore time because it has no date)
- realtime_logs:
- type: remap
- inputs:
- - realtime_logs_filtered
- source: |-
- .metadata.project = del(.project)
- .metadata.external_id = .metadata.project
- parsed, err = parse_regex(.event_message, r'^(?P<time>\d+:\d+:\d+\.\d+) \[(?P<level>\w+)\] (?P<msg>.*)$')
- if err == null {
- .event_message = parsed.msg
- .metadata.level = parsed.level
- }
- # Function logs are unstructured messages on stderr
- functions_logs:
- type: remap
- inputs:
- - router.functions
- source: |-
- .metadata.project_ref = del(.project)
- # Storage logs may contain json objects so we parse them for completeness
- storage_logs:
- type: remap
- inputs:
- - router.storage
- source: |-
- .metadata.project = del(.project)
- .metadata.tenantId = .metadata.project
- parsed, err = parse_json(.event_message)
- if err == null {
- .event_message = parsed.msg
- .metadata.level = parsed.level
- .metadata.timestamp = parsed.time
- .metadata.context[0].host = parsed.hostname
- .metadata.context[0].pid = parsed.pid
- }
- # Postgres logs some messages to stderr which we map to warning severity level
- db_logs:
- type: remap
- inputs:
- - router.db
- source: |-
- .metadata.host = "db-default"
- .metadata.parsed.timestamp = .timestamp
- parsed, err = parse_regex(.event_message, r'.*(?P<level>INFO|NOTICE|WARNING|ERROR|LOG|FATAL|PANIC?):.*', numeric_groups: true)
- if err != null || parsed == null {
- .metadata.parsed.error_severity = "info"
- }
- if parsed.level != null {
- .metadata.parsed.error_severity = parsed.level
- }
- if .metadata.parsed.error_severity == "info" {
- .metadata.parsed.error_severity = "log"
- }
- .metadata.parsed.error_severity = upcase!(.metadata.parsed.error_severity)
- sinks:
- logflare_auth:
- type: 'http'
- inputs:
- - auth_logs
- encoding:
- codec: 'json'
- method: 'post'
- request:
- retry_max_duration_secs: 30
- retry_initial_backoff_secs: 1
- headers:
- x-api-key: ${LOGFLARE_PUBLIC_ACCESS_TOKEN?LOGFLARE_PUBLIC_ACCESS_TOKEN is required}
- uri: 'http://analytics:4000/api/logs?source_name=gotrue.logs.prod'
- logflare_realtime:
- type: 'http'
- inputs:
- - realtime_logs
- encoding:
- codec: 'json'
- method: 'post'
- request:
- retry_max_duration_secs: 30
- retry_initial_backoff_secs: 1
- headers:
- x-api-key: ${LOGFLARE_PUBLIC_ACCESS_TOKEN?LOGFLARE_PUBLIC_ACCESS_TOKEN is required}
- uri: 'http://analytics:4000/api/logs?source_name=realtime.logs.prod'
- logflare_rest:
- type: 'http'
- inputs:
- - rest_logs
- encoding:
- codec: 'json'
- method: 'post'
- request:
- retry_max_duration_secs: 30
- retry_initial_backoff_secs: 1
- headers:
- x-api-key: ${LOGFLARE_PUBLIC_ACCESS_TOKEN?LOGFLARE_PUBLIC_ACCESS_TOKEN is required}
- uri: 'http://analytics:4000/api/logs?source_name=postgREST.logs.prod'
- logflare_db:
- type: 'http'
- inputs:
- - db_logs
- encoding:
- codec: 'json'
- method: 'post'
- request:
- retry_max_duration_secs: 30
- retry_initial_backoff_secs: 1
- headers:
- x-api-key: ${LOGFLARE_PUBLIC_ACCESS_TOKEN?LOGFLARE_PUBLIC_ACCESS_TOKEN is required}
- # Route directly to analytics like other sinks. Retry handles the startup
- # race where analytics may not be ready yet when early DB logs arrive.
- uri: 'http://analytics:4000/api/logs?source_name=postgres.logs'
- logflare_functions:
- type: 'http'
- inputs:
- - functions_logs
- encoding:
- codec: 'json'
- method: 'post'
- request:
- retry_max_duration_secs: 30
- retry_initial_backoff_secs: 1
- headers:
- x-api-key: ${LOGFLARE_PUBLIC_ACCESS_TOKEN?LOGFLARE_PUBLIC_ACCESS_TOKEN is required}
- uri: 'http://analytics:4000/api/logs?source_name=deno-relay-logs'
- logflare_storage:
- type: 'http'
- inputs:
- - storage_logs
- encoding:
- codec: 'json'
- method: 'post'
- request:
- retry_max_duration_secs: 30
- retry_initial_backoff_secs: 1
- headers:
- x-api-key: ${LOGFLARE_PUBLIC_ACCESS_TOKEN?LOGFLARE_PUBLIC_ACCESS_TOKEN is required}
- uri: 'http://analytics:4000/api/logs?source_name=storage.logs.prod.2'
- logflare_kong:
- type: 'http'
- inputs:
- - kong_logs
- - kong_err
- encoding:
- codec: 'json'
- method: 'post'
- request:
- retry_max_duration_secs: 30
- retry_initial_backoff_secs: 1
- headers:
- x-api-key: ${LOGFLARE_PUBLIC_ACCESS_TOKEN?LOGFLARE_PUBLIC_ACCESS_TOKEN is required}
- uri: 'http://analytics:4000/api/logs?source_name=cloudflare.logs.prod'
|