MySQL Logs Monitoring with OpenTelemetry Collector

SigNoz Cloud - This page applies to SigNoz Cloud editions.
Self-Host - This page applies to self-hosted SigNoz editions.

Overview

Collect MySQL error, slow query, and general query logs with the OpenTelemetry Collector and send them to SigNoz. Start with the error log, which most MySQL packages already write to a file, then add the other logs and field parsing as you need them.

Prerequisites

  • A MySQL 8.0+ server that you can access, and restart if you enable the slow query log later
  • An OpenTelemetry Collector v0.144.0+ on the MySQL host, running as a user with read access to the log files

Send your first MySQL logs

MySQL packages on most Linux distributions already write the error log to a file, so you can ship it without editing MySQL configuration or restarting the server.

Step 1: Locate the error log

Connect to MySQL with a client such as mysql and ask where it writes the error log:

SHOW VARIABLES LIKE 'log_error';

An absolute path means MySQL writes to a file the Collector can tail. A value of stderr means MySQL writes to the console instead, so set a file path under [mysqld] in your MySQL configuration file (/etc/mysql/my.cnf or /etc/my.cnf, depending on your distribution) and restart MySQL:

my.cnf
[mysqld]
log_error = /var/log/mysql/error.log

Keep log_output set to FILE, its default. MySQL can write logs to database tables instead, which the filelog receiver cannot read.

Step 2: Create the Collector config file

Create a file named mysql-logs-collection-config.yaml:

mysql-logs-collection-config.yaml
receivers:
  filelog/mysql_error:
    include: ["${env:MYSQL_ERROR_LOG}"]
    # Ship the log file's existing contents on the first run so you see data
    # right away. Remove this line once the pipeline works.
    start_at: beginning
    operators:
      - type: add
        field: attributes.source
        value: mysql
      - type: add
        field: attributes.log_type
        value: error

processors:
  batch:
    send_batch_size: 10000
    send_batch_max_size: 11000
    timeout: 10s

exporters:
  # Export to SigNoz Cloud over OTLP/HTTP.
  otlp_http/mysql-logs:
    endpoint: "${env:OTLP_DESTINATION_ENDPOINT}"
    headers:
      "signoz-ingestion-key": "${env:SIGNOZ_INGESTION_KEY}"

service:
  pipelines:
    logs/mysql:
      receivers: [filelog/mysql_error]
      processors: [batch]
      exporters: [otlp_http/mysql-logs]

Each line arrives in SigNoz as raw text, tagged with source and log_type so you can tell MySQL logs apart from other sources. To pull out the timestamp, severity, and message, see Parse the logs into fields.

Step 3: Run the Collector

The config above is the same everywhere. Only file access and startup differ by environment.

Set the environment variables referenced in the config:

# Absolute path to the MySQL error log, readable by the Collector
export MYSQL_ERROR_LOG="/var/log/mysql/error.log"

# Region-specific SigNoz Cloud OTLP/HTTP endpoint
export OTLP_DESTINATION_ENDPOINT="https://ingest.<region>.signoz.cloud:443"

# your SigNoz ingestion key
export SIGNOZ_INGESTION_KEY="<your-ingestion-key>"

Verify these values:

Start the Collector with the config file. It accepts multiple --config flags, so you can run this alongside your metrics config:

otelcol-contrib --config mysql-logs-collection-config.yaml

Optional configuration

The steps above ship raw error log lines. Expand any of these to parse those lines into fields, or to collect MySQL's other log files.

Parse the logs into fields

The minimal config sends each error log line as raw text. Add regex_parser operators to pull out the timestamp, severity, and message so you can filter and aggregate on them in SigNoz.

Replace the operators block of filelog/mysql_error with:

mysql-logs-collection-config.yaml
    operators:
      # Error log lines look like:
      # 2020-08-06T14:25:02.835618Z 0 [Warning] [MY-010068] [Server] <message>
      - type: regex_parser
        parse_from: body
        regex: '^(?P<ts>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z)\s+(?P<thread_id>\d+)\s+\[(?P<log_level>[^\]]+)\]\s+\[(?P<error_code>[^\]]+)\]\s+\[(?P<subsystem>[^\]]+)\]\s+(?P<message>.*)$'
        timestamp:
          parse_from: attributes.ts
          layout: '%Y-%m-%dT%H:%M:%S.%fZ'
        severity:
          parse_from: attributes.log_level
          mapping:
            error: ERROR
            warn: Warning
            info:
              - Note
              - System
        on_error: send
      - type: move
        if: attributes.message != nil
        from: attributes.message
        to: body
      - type: remove
        if: attributes.ts != nil
        field: attributes.ts
      - type: add
        field: attributes.source
        value: mysql
      - type: add
        field: attributes.log_type
        value: error

Restart the Collector. New entries arrive with thread_id, error_code, subsystem, and severity_text as attributes. on_error: send sends unparseable lines through with their raw text intact instead of dropping them.

Collect slow query logs

The slow query log records statements that run longer than long_query_time seconds. It is off by default.

Enable the slow query log

Set these under [mysqld] in your MySQL configuration file and restart MySQL:

my.cnf
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 2
# Optional: log statements that run without an index
log_queries_not_using_indexes = 1

For a quick test, set these at runtime with SET GLOBAL instead, which needs no restart. MySQL marks log_error as non-dynamic, so the error log path always requires a restart.

MySQL's own default for slow_query_log_file is <hostname>-slow.log in the data directory (typically /var/lib/mysql/). Use any absolute path the Collector can read.

Add the receiver

Add a second receiver to mysql-logs-collection-config.yaml and include it in the pipeline:

mysql-logs-collection-config.yaml
receivers:
  filelog/mysql_slow:
    include: ["${env:MYSQL_SLOW_LOG}"]
    # Each entry starts with "# Time:" and spans several lines. Recombine
    # them into one log record for parsing.
    multiline:
      line_start_pattern: '^# Time:'
    operators:
      - type: regex_parser
        parse_from: body
        regex: '# Time: (?P<ts>\S+)\n# User@Host:\s+(?P<user>\S+)\s+@\s+(?P<host>\S*)\s+\[(?P<ip>[^\]]*)\](?:\s+Id:\s+(?P<conn_id>\d+))?\n# Query_time:\s+(?P<query_time>[\d.]+)\s+Lock_time:\s+(?P<lock_time>[\d.]+)\s+Rows_sent:\s+(?P<rows_sent>\d+)\s+Rows_examined:\s+(?P<rows_examined>\d+)\n(?:SET timestamp=\d+;\n)?(?P<query>(?s:.*))'
        timestamp:
          parse_from: attributes.ts
          layout: '%Y-%m-%dT%H:%M:%S.%fZ'
        on_error: send
      - type: remove
        if: attributes.ts != nil
        field: attributes.ts
      - type: add
        field: attributes.source
        value: mysql
      - type: add
        field: attributes.log_type
        value: slow_query

service:
  pipelines:
    logs/mysql:
      receivers: [filelog/mysql_error, filelog/mysql_slow]

Set MYSQL_SLOW_LOG to the configured slow_query_log_file path, then restart the Collector:

export MYSQL_SLOW_LOG="/var/log/mysql/mysql-slow.log"

Run SELECT SLEEP(3); to generate an entry, then filter by log_type = slow_query in SigNoz. Entries carry query_time, lock_time, rows_sent, rows_examined, user, and the statement itself.

Collect general query logs

The general query log records client connections and statements received by MySQL. It is verbose and has a measurable performance cost, so keep it off in production and enable it for short debugging sessions.

my.cnf
[mysqld]
general_log = 1
general_log_file = /var/log/mysql/mysql.log

Entries are tab-separated with Time, Id (connection ID), Command, and Argument:

2024-01-15T10:45:23.000000Z	   42	Query	SELECT * FROM users WHERE id = 1
2024-01-15T10:45:26.000000Z	   43	Connect	root@localhost on  using TCP/IP

Add a filelog/mysql_general receiver pointed at general_log_file and include it in the logs pipeline. A minimal receiver that keeps the raw line in body and tags the source:

mysql-logs-collection-config.yaml
receivers:
  filelog/mysql_general:
    include: ["${env:MYSQL_GENERAL_LOG}"]
    operators:
      - type: add
        field: attributes.source
        value: mysql
      - type: add
        field: attributes.log_type
        value: general

Set MYSQL_GENERAL_LOG to the configured general_log_file path, then add filelog/mysql_general to service.pipelines.logs/mysql.receivers.

Validate

  1. Run FLUSH LOGS; in a MySQL client, or restart MySQL, to write a fresh error log entry.
  2. Open Logs > Explorer in SigNoz and filter by source = mysql.
  3. Confirm that error log lines appear, tagged with log_type = error.

With start_at: beginning, the error log's existing entries also arrive within seconds of the Collector starting.

MySQL logs in SigNoz Logs Explorer filtered by source = mysql
MySQL logs in SigNoz, filtered by source = mysql

Troubleshooting

No logs in SigNoz: Run tail against each configured log path as the Collector's OS user. Check the Collector logs for permission denied or no such file errors, and verify MYSQL_ERROR_LOG and MYSQL_SLOW_LOG point to the real paths (SHOW VARIABLES LIKE 'slow_query_log_file';).

Slow-query entries split across multiple records: The multiline.line_start_pattern must match the start of each entry. Confirm your slow log begins each block with # Time:. If the last entry is delayed, set force_flush_period: 5s on the receiver.

Fields not parsed (raw line in body): The regex targets the MySQL 8.0 format. On MySQL 5.7 the error-log line has no [MY-code]/[subsystem] fields; drop those groups from the regex. With on_error: send, unparsed lines reach SigNoz with the raw text in body.

Logs missing from the file: The slow and general query logs are off by default. Verify SHOW VARIABLES LIKE 'slow_query_log'; returns ON and that log_output includes FILE (SHOW VARIABLES LIKE 'log_output';).

Next Steps

Get Help

If you need help with the steps in this topic, please reach out to us on SigNoz Community Slack. If you are a SigNoz Cloud user, please use in product chat support located at the bottom right corner of your SigNoz instance or contact us at cloud-support@signoz.io.

Last updated: July 22, 2026

Edit on GitHub

Was this page helpful?

Your response helps us improve this page.