// Copyright 2015 CoreOS, Inc.//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// http://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License.
// Package journal provides write bindings to the local systemd journal.// It is implemented in pure Go and connects to the journal directly over its// unix socket.//// To read from the journal, see the "sdjournal" package, which wraps the// sd-journal a C API.//// http://www.freedesktop.org/software/systemd/man/systemd-journald.service.html
package journalimport ()// Priority of a journal messagetypePriorityintconst (PriEmergPriority = iotaPriAlertPriCritPriErrPriWarningPriNoticePriInfoPriDebug)var (// This can be overridden at build-time: // https://github.com/golang/go/wiki/GcToolchainTricks#including-build-information-in-the-executable journalSocket = "/run/systemd/journal/socket"// unixConnPtr atomically holds the local unconnected Unix-domain socket. // Concrete safe pointer type: *net.UnixConn unixConnPtr unsafe.Pointer// onceConn ensures that unixConnPtr is initialized exactly once. onceConn sync.Once)func init() {onceConn.Do(initConn)}// Enabled checks whether the local systemd journal is available for logging.func () bool {onceConn.Do(initConn)if (*net.UnixConn)(atomic.LoadPointer(&unixConnPtr)) == nil {returnfalse }if , := net.Dial("unixgram", journalSocket); != nil {returnfalse }returntrue}// Send a message to the local systemd journal. vars is a map of journald// fields to values. Fields must be composed of uppercase letters, numbers,// and underscores, but must not start with an underscore. Within these// restrictions, any arbitrary field name may be used. Some names have special// significance: see the journalctl documentation// (http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html)// for more details. vars may be nil.func ( string, Priority, map[string]string) error { := (*net.UnixConn)(atomic.LoadPointer(&unixConnPtr))if == nil {returnerrors.New("could not initialize socket to journald") } := &net.UnixAddr{Name: journalSocket,Net: "unixgram", } := new(bytes.Buffer)appendVariable(, "PRIORITY", strconv.Itoa(int()))appendVariable(, "MESSAGE", )for , := range {appendVariable(, , ) } , , := .WriteMsgUnix(.Bytes(), nil, )if == nil {returnnil }if !isSocketSpaceError() {return }// Large log entry, send it via tempfile and ancillary-fd. , := tempFd()if != nil {return }defer .Close() _, = io.Copy(, )if != nil {return } := syscall.UnixRights(int(.Fd())) _, _, = .WriteMsgUnix([]byte{}, , )if != nil {return }returnnil}// Print prints a message to the local systemd journal using Send().func ( Priority, string, ...interface{}) error {returnSend(fmt.Sprintf(, ...), , nil)}func appendVariable( io.Writer, , string) {if := validVarName(); != nil {fmt.Fprintf(os.Stderr, "variable name %s contains invalid character, ignoring\n", ) }ifstrings.ContainsRune(, '\n') {/* When the value contains a newline, we write: * - the variable name, followed by a newline * - the size (in 64bit little endian format) * - the data, followed by a newline */fmt.Fprintln(, )binary.Write(, binary.LittleEndian, uint64(len()))fmt.Fprintln(, ) } else {/* just write the variable and value all on one line */fmt.Fprintf(, "%s=%s\n", , ) }}// validVarName validates a variable name to make sure journald will accept it.// The variable name must be in uppercase and consist only of characters,// numbers and underscores, and may not begin with an underscore:// https://www.freedesktop.org/software/systemd/man/sd_journal_print.htmlfunc validVarName( string) error {if == "" {returnerrors.New("Empty variable name") } elseif [0] == '_' {returnerrors.New("Variable name begins with an underscore") }for , := range {if !(('A' <= && <= 'Z') || ('0' <= && <= '9') || == '_') {returnerrors.New("Variable name contains invalid characters") } }returnnil}// isSocketSpaceError checks whether the error is signaling// an "overlarge message" condition.func isSocketSpaceError( error) bool { , := .(*net.OpError)if ! || == nil {returnfalse } , := .Err.(*os.SyscallError)if ! || == nil {returnfalse }return .Err == syscall.EMSGSIZE || .Err == syscall.ENOBUFS}// tempFd creates a temporary, unlinked file under `/dev/shm`.func tempFd() (*os.File, error) { , := ioutil.TempFile("/dev/shm/", "journal.XXXXX")if != nil {returnnil, } = syscall.Unlink(.Name())if != nil {returnnil, }return , nil}// initConn initializes the global `unixConnPtr` socket.// It is meant to be called exactly once, at program startup.func initConn() { , := net.ResolveUnixAddr("unixgram", "")if != nil {return } , := net.ListenUnixgram("unixgram", )if != nil {return }atomic.StorePointer(&unixConnPtr, unsafe.Pointer())}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.