set syslog priority and do not write level and date to syslog (#4923) (#5296)
Some checks are pending
code_lint / go (push) Waiting to run
code_lint / go_mod (push) Waiting to run
code_lint / docs (push) Waiting to run
code_lint / api_docs (push) Waiting to run
code_test / test_64 (push) Waiting to run
code_test / test_32 (push) Waiting to run
code_test / test_e2e (push) Waiting to run

When writing log entries to syslog, pass the level (WARN, INFO, etc)
directly to syslog. Avoid writing level and date. This provides a
better integration and allows to use syslog search tools.
This commit is contained in:
Alessandro Ros 2025-12-27 14:06:26 +01:00 committed by GitHub
parent d9917feb76
commit 3692b2448a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 109 additions and 110 deletions

View file

@ -1,35 +0,0 @@
# Log management
By default, log entries are printed on the console (stdout). It is possible to write logs to a file by using the `logDestinations` and `logFile` settings:
```yml
# Destinations of log messages; available values are "stdout", "file" and "syslog".
logDestinations: [file]
# If "file" is in logDestinations, this is the file which will receive the logs.
logFile: mediamtx.log
```
The log file can be periodically rotated or truncated by using an external utility.
On most Linux distributions, the `logrotate` utility is in charge of managing log files. It can be configured to handle the _MediaMTX_ log file too by creating a configuration file, placed in `/etc/logrotate.d/mediamtx`, with this content:
```
/my/mediamtx/path/mediamtx.log {
daily
copytruncate
rotate 7
compress
delaycompress
missingok
notifempty
}
```
This file will rotate the log file every day, adding a `.NUMBER` suffix to older copies:
```
mediamtx.log.1
mediamtx.log.2
mediamtx.log.3
...
```

View file

@ -0,0 +1,71 @@
# Logging
## Log verbosity
Log verbosity can be set with the `logLevel` parameter:
```yml
# Verbosity of the program; available values are "error", "warn", "info", "debug".
logLevel: info
```
## Log destinations
Log entries can be sent to multiple destinations. By default, they are printed on the console (stdout).
It is possible to write logs to a file by using these parameters:
```yml
# Destinations of log messages; available values are "stdout", "file" and "syslog".
logDestinations: [file]
# If "file" is in logDestinations, this is the file which will receive the logs.
logFile: mediamtx.log
```
It is possible to write logs to the system logging server (syslog) by using these parameters:
```yml
# Destinations of log messages; available values are "stdout", "file" and "syslog".
logDestinations: [syslog]
# If "syslog" is in logDestinations, use prefix for logs.
sysLogPrefix: mediamtx
```
Log entries can be queried by using:
```sh
journalctl SYSLOG_IDENTIFIER=mediamtx
```
If _MediaMTX_ is also running as a [system service](start-on-boot), log entries can be queried by using:
```sh
journalctl -u mediamtx
```
## Log file rotation
The log file can be periodically rotated or truncated by using an external utility.
On most Linux distributions, the `logrotate` utility is in charge of managing log files. It can be configured to handle the _MediaMTX_ log file too by creating a configuration file, placed in `/etc/logrotate.d/mediamtx`, with this content:
```
/my/mediamtx/path/mediamtx.log {
daily
copytruncate
rotate 7
compress
delaycompress
missingok
notifempty
}
```
This file will rotate the log file every day, adding a `.NUMBER` suffix to older copies:
```
mediamtx.log.1
mediamtx.log.2
mediamtx.log.3
...
```

View file

@ -2,6 +2,7 @@ package logger
import (
"bytes"
"fmt"
"os"
"time"
)
@ -26,7 +27,8 @@ func (d *destinationFile) log(t time.Time, level Level, format string, args ...a
d.buf.Reset()
writeTime(&d.buf, t, false)
writeLevel(&d.buf, level, false)
writeContent(&d.buf, format, args)
fmt.Fprintf(&d.buf, format, args...)
d.buf.WriteByte('\n')
d.file.Write(d.buf.Bytes()) //nolint:errcheck
}

View file

@ -2,6 +2,7 @@ package logger
import (
"bytes"
"fmt"
"os"
"time"
@ -10,8 +11,7 @@ import (
type destinationStdout struct {
useColor bool
buf bytes.Buffer
buf bytes.Buffer
}
func newDestionationStdout() destination {
@ -24,7 +24,8 @@ func (d *destinationStdout) log(t time.Time, level Level, format string, args ..
d.buf.Reset()
writeTime(&d.buf, t, d.useColor)
writeLevel(&d.buf, level, d.useColor)
writeContent(&d.buf, format, args)
fmt.Fprintf(&d.buf, format, args...)
d.buf.WriteByte('\n')
os.Stdout.Write(d.buf.Bytes()) //nolint:errcheck
}

View file

@ -1,18 +1,21 @@
//go:build !darwin && !windows
package logger
import (
"bytes"
"io"
"fmt"
"log/syslog"
"time"
)
type destinationSysLog struct {
syslog io.WriteCloser
syslog *syslog.Writer
buf bytes.Buffer
}
func newDestinationSyslog(prefix string) (destination, error) {
syslog, err := newSysLog(prefix)
syslog, err := syslog.New(syslog.LOG_DAEMON, prefix)
if err != nil {
return nil, err
}
@ -22,14 +25,23 @@ func newDestinationSyslog(prefix string) (destination, error) {
}, nil
}
func (d *destinationSysLog) log(t time.Time, level Level, format string, args ...any) {
func (d *destinationSysLog) log(_ time.Time, level Level, format string, args ...any) {
d.buf.Reset()
writeTime(&d.buf, t, false)
writeLevel(&d.buf, level, false)
writeContent(&d.buf, format, args)
d.syslog.Write(d.buf.Bytes()) //nolint:errcheck
fmt.Fprintf(&d.buf, format, args...)
switch level {
case Debug:
d.syslog.Debug(d.buf.String()) //nolint:errcheck
case Info:
d.syslog.Info(d.buf.String()) //nolint:errcheck
case Warn:
d.syslog.Warning(d.buf.String()) //nolint:errcheck
case Error:
d.syslog.Err(d.buf.String()) //nolint:errcheck
}
}
func (d *destinationSysLog) close() {
d.syslog.Close()
d.syslog.Close() //nolint:errcheck
}

View file

@ -0,0 +1,9 @@
//go:build darwin || windows
package logger
import "fmt"
func newDestinationSyslog(_ string) (destination, error) {
return nil, fmt.Errorf("syslog is not available on macOS and Windows")
}

View file

@ -3,7 +3,6 @@ package logger
import (
"bytes"
"fmt"
"sync"
"time"
@ -135,11 +134,6 @@ func writeLevel(buf *bytes.Buffer, level Level, useColor bool) {
buf.WriteByte(' ')
}
func writeContent(buf *bytes.Buffer, format string, args []any) {
fmt.Fprintf(buf, format, args...)
buf.WriteByte('\n')
}
// Log writes a log entry.
func (lh *Logger) Log(level Level, format string, args ...any) {
if level < lh.level {

View file

@ -1,12 +0,0 @@
//go:build darwin
package logger
import (
"fmt"
"io"
)
func newSysLog(_ string) (io.WriteCloser, error) {
return nil, fmt.Errorf("unavailable on macOS")
}

View file

@ -1,31 +0,0 @@
//go:build !windows && !darwin
package logger
import (
"io"
native "log/syslog"
)
type syslog struct {
inner *native.Writer
}
func newSysLog(prefix string) (io.WriteCloser, error) {
inner, err := native.New(native.LOG_INFO|native.LOG_DAEMON, prefix)
if err != nil {
return nil, err
}
return &syslog{
inner: inner,
}, nil
}
func (ls *syslog) Close() error {
return ls.inner.Close()
}
func (ls *syslog) Write(p []byte) (int, error) {
return ls.inner.Write(p)
}

View file

@ -1,12 +0,0 @@
//go:build windows
package logger
import (
"fmt"
"io"
)
func newSysLog(_ string) (io.WriteCloser, error) {
return nil, fmt.Errorf("unavailable on Windows")
}

View file

@ -10,7 +10,7 @@
logLevel: info
# Destinations of log messages; available values are "stdout", "file" and "syslog".
logDestinations: [stdout]
# If "file" is in logDestinations, this is the file which will receive the logs.
# If "file" is in logDestinations, this is the file which will receive logs.
logFile: mediamtx.log
# If "syslog" is in logDestinations, use prefix for logs.
sysLogPrefix: mediamtx