diff --git a/docs/2-usage/17-log-management.md b/docs/2-usage/17-log-management.md deleted file mode 100644 index cf0d7ef2..00000000 --- a/docs/2-usage/17-log-management.md +++ /dev/null @@ -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 -... -``` diff --git a/docs/2-usage/17-logging.md b/docs/2-usage/17-logging.md new file mode 100644 index 00000000..8a218719 --- /dev/null +++ b/docs/2-usage/17-logging.md @@ -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 +... +``` diff --git a/internal/logger/destination_file.go b/internal/logger/destination_file.go index 08e8a89a..928125c8 100644 --- a/internal/logger/destination_file.go +++ b/internal/logger/destination_file.go @@ -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 } diff --git a/internal/logger/destination_stdout.go b/internal/logger/destination_stdout.go index 43c11bed..f6b8d137 100644 --- a/internal/logger/destination_stdout.go +++ b/internal/logger/destination_stdout.go @@ -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 } diff --git a/internal/logger/destination_syslog.go b/internal/logger/destination_syslog.go index e9a8d4ed..9540e65b 100644 --- a/internal/logger/destination_syslog.go +++ b/internal/logger/destination_syslog.go @@ -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 } diff --git a/internal/logger/destination_syslog_disabled.go b/internal/logger/destination_syslog_disabled.go new file mode 100644 index 00000000..d0788b24 --- /dev/null +++ b/internal/logger/destination_syslog_disabled.go @@ -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") +} diff --git a/internal/logger/logger.go b/internal/logger/logger.go index a6a60cc1..c2210bdf 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -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 { diff --git a/internal/logger/syslog_darwin.go b/internal/logger/syslog_darwin.go deleted file mode 100644 index 335cfb66..00000000 --- a/internal/logger/syslog_darwin.go +++ /dev/null @@ -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") -} diff --git a/internal/logger/syslog_unix.go b/internal/logger/syslog_unix.go deleted file mode 100644 index 0b07040e..00000000 --- a/internal/logger/syslog_unix.go +++ /dev/null @@ -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) -} diff --git a/internal/logger/syslog_win.go b/internal/logger/syslog_win.go deleted file mode 100644 index 7468d83e..00000000 --- a/internal/logger/syslog_win.go +++ /dev/null @@ -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") -} diff --git a/mediamtx.yml b/mediamtx.yml index 8b20bfa6..315de7a5 100644 --- a/mediamtx.yml +++ b/mediamtx.yml @@ -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