Problem: You want to log in to the system log instead of your logfiles.
Solution: Use the log/syslog package to write to syslog.
Syslog is a standard network-based logging protocol. It has long been the de facto standard for logging system events and was created by Eric Allman in the 1980s as part of the Sendmail project. The protocol was documented in RFC 3164 by the Internet Engineering Task Force (IETF). Subsequently, IETF standardized it in RFC 5424.
A syslog message (as in RFC 3164) consists of three parts:
Priority
• Includes the facility and the severity
Header
• Includes the timestamp and the hostname or IP address of the machine
Message
• Includes the tag and the content
The facility describes the type of system that sends the log message. It allows log messages from different facilities to be handled differently. There are 24 facilities defined by the RFCs; here are a few:
• 0 (kernel)
• 1 (user-level)
• 2 (mail)
• 3 (system daemons)
• 4 (security/authorization messages)
The severity level is similar to the log level. Syslog defines eight different levels, with 0 being the highest and 7 being the lowest:
• 0 (Emergency)
• 1 (Alert)
• 2 (Critical)
• 3 (Error)
• 4 (Warning)
• 5 (Notice)
• 6 (Informational)
• 7 (Debug)
The timestamp and the hostname or IP address are self-explanatory. The tag is the name of the program that generated the message, while the content is the details of the log message.
Syslog is not implemented uniformly in different operating systems. A popular implementation of syslog is rsyslog, the default syslog implementation in many Linux variants including Debian, Ubuntu, openSUSE, and others.
Go provides a log/syslog package as part of the standard library to interface with syslog. However, it doesn’t work on all systems. For example, it doesn’t work with Windows because it’s not implemented on Windows.
The example in this recipe is based on running against rsyslog on Ubuntu 20.04, and it should work on systems with rsyslog. However, I have not tried it on all systems and implementations.
Before we start on the Go code, you need to set up rsyslog to show the priority, header, and message parts. In rsyslog this is done using a template in the rsyslog configuration file.
Start by editing the /etc/rsyslog.conf configuration file:
$ sudo vi /etc/rsyslog.conf
Add the template configuration after this line — $ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat in the configuration file:
$template gosyslogs,"%syslogseverity-text% %syslogfacility-text% %hostname% %timegenerated% %syslogtag% %msg%\n" $ActionFileDefaultTemplate gosyslogs
In this configuration, you name the template gosyslogs . You set it to show the severity first, followed by the facility, then the hostname and the timestamp, and finally, the tag and message.
Once you save this file, restart rsyslog:
sudo service rsyslog restart
Now that you have set up rsyslog, you can look at the code. Sending log messages to syslog using the syslog package is relatively straightforward:
var logger *log.Logger func init() { var err error logger, err = syslog.NewLogger(syslog.LOG_USER | syslog.LOG_NOTICE, 0) if err != nil { log.Fatal("cannot write to syslog: ", err) } } func main() { logger.Print("hello syslog!") }
You use the NewLogger function to create a logger, passing the syslog priority flags you want to set. The syslog package provides flags for the facility and the severity levels. You can or them together to send the facility code and the severity level. For the case of the preceding code, you send in syslog.LOG_USER indicating the user facility code, and syslog.LOG_NOTICE indicating the notice severity level.
Run the code first in the file named main.go :
$ go run main.go
Now check the syslogs. Run this on the command line:
$ sudo tail /var/log/syslog
Logs looked like below before modifying /etc/rsyslog.conf:
zzh@ZZHPC:~$ sudo tail -f /var/log/syslog Oct 23 19:10:40 ZZHPC systemd[1]: Stopping System Logging Service... Oct 23 19:10:40 ZZHPC rsyslogd: [origin software="rsyslogd" swVersion="8.2112.0" x-pid="10021" x-info="https://www.rsyslog.com"] exiting on signal 15. Oct 23 19:10:40 ZZHPC systemd[1]: rsyslog.service: Deactivated successfully. Oct 23 19:10:40 ZZHPC systemd[1]: Stopped System Logging Service. Oct 23 19:10:40 ZZHPC systemd[1]: Starting System Logging Service... Oct 23 19:10:40 ZZHPC rsyslogd: imuxsock: Acquired UNIX socket '/run/systemd/journal/syslog' (fd 3) from systemd. [v8.2112.0] Oct 23 19:10:40 ZZHPC rsyslogd: rsyslogd's groupid changed to 111 Oct 23 19:10:40 ZZHPC systemd[1]: Started System Logging Service. Oct 23 19:10:40 ZZHPC rsyslogd: rsyslogd's userid changed to 104 Oct 23 19:10:40 ZZHPC rsyslogd: [origin software="rsyslogd" swVersion="8.2112.0" x-pid="11847" x-info="https://www.rsyslog.com"] start Oct 23 19:11:07 ZZHPC wpa_supplicant[686]: wlo1: CTRL-EVENT-SIGNAL-CHANGE above=0 signal=-66 noise=9999 txrate=115600 Oct 23 19:11:08 ZZHPC wpa_supplicant[686]: wlo1: CTRL-EVENT-SIGNAL-CHANGE above=1 signal=-63 noise=9999 txrate=115600
Logs look like below after modifying /etc/rsyslog.conf and restarting rsyslog service:
zzh@ZZHPC:~$ sudo tail -f /var/log/syslog notice daemon ZZHPC Oct 23 18:45:24 wpa_supplicant[686]: wlo1: CTRL-EVENT-SIGNAL-CHANGE above=0 signal=-63 noise=9999 txrate=130000 notice daemon ZZHPC Oct 23 18:45:28 wpa_supplicant[686]: wlo1: CTRL-EVENT-SIGNAL-CHANGE above=1 signal=-65 noise=9999 txrate=115600 notice daemon ZZHPC Oct 23 18:45:29 wpa_supplicant[686]: wlo1: CTRL-EVENT-SIGNAL-CHANGE above=0 signal=-64 noise=9999 txrate=130000 notice daemon ZZHPC Oct 23 18:45:31 wpa_supplicant[686]: wlo1: CTRL-EVENT-SIGNAL-CHANGE above=0 signal=-64 noise=9999 txrate=130000 notice daemon ZZHPC Oct 23 18:45:44 wpa_supplicant[686]: wlo1: CTRL-EVENT-SIGNAL-CHANGE above=1 signal=-65 noise=9999 txrate=115600 notice daemon ZZHPC Oct 23 18:45:45 wpa_supplicant[686]: wlo1: CTRL-EVENT-SIGNAL-CHANGE above=0 signal=-73 noise=9999 txrate=130000 notice daemon ZZHPC Oct 23 18:45:49 wpa_supplicant[686]: wlo1: CTRL-EVENT-SIGNAL-CHANGE above=1 signal=-65 noise=9999 txrate=130000 info user ZZHPC Oct 23 18:49:04 google-chrome.desktop[5759]: Fontconfig error: Cannot load default config file: No such file: (null) info user ZZHPC Oct 23 18:57:15 code.desktop[6419]: #033[90m[main 2023-10-23T10:57:15.567Z]#033[0m update#setState checking for updates info user ZZHPC Oct 23 18:57:16 code.desktop[6419]: #033[90m[main 2023-10-23T10:57:16.234Z]#033[0m update#setState idle notice user ZZHPC Oct 23 19:08:05 /tmp/go-build117951185/b001/exe/main[11641]: hello syslog!
The first field is notice , the severity level, followed by user , which is the facility code. Next is ZZHPC , which is the hostname, followed by the timestamp.
The next field is the tag , which is the /tmp/go-build117951185/b001/exe/main[11641] field in the log message. Why is it indicating that it’s in the /tmp directory? That’s because you’re using go run . It will look different if you compile the code and run the binary file. The final field in the log message is the details of the log, which you print out using logger .
package main import ( "log" "log/syslog" ) func main() { sysLog, err := syslog.New(syslog.LOG_SYSLOG, "system_log.go") // const syslog.LOG_SYSLOG syslog.Priority = 40 if err != nil { log.Println(err) return } else { log.SetOutput(sysLog) log.Print("Everything is fine!") } } // journalctl -xe | tail -1 // Jun 10 09:51:12 ZZHPC system_log.go[13989]: 2024/06/10 09:51:12 Everything is fine!