blob: ca7a0a0f4fb5e91bbf293c8cccd43663d06b43c4 [file] [log] [blame]
Serge Bazanski5faa2fc2020-09-07 14:09:30 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package logtree
18
19import (
20 "fmt"
Serge Bazanskib0272182020-11-02 18:39:44 +010021 "strconv"
22 "strings"
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020023 "time"
Serge Bazanskib0272182020-11-02 18:39:44 +010024
25 apb "git.monogon.dev/source/nexantic.git/core/proto/api"
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020026)
27
Serge Bazanski1bfa0c22020-10-14 16:45:07 +020028// LeveledPayload is a log entry for leveled logs (as per leveled.go). It contains not only the log message itself and
29// its severity, but also additional metadata that would be usually seen in a text representation of a leveled log entry.
30type LeveledPayload struct {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020031 // message is the log message, rendered from a leveled log call like Infof(), Warningf(), ...
32 message string
33 // timestamp is the time at which this message was emitted.
34 timestamp time.Time
35 // severity is the leveled Severity at which this message was emitted.
36 severity Severity
37 // file is the filename of the caller that emitted this message.
38 file string
39 // line is the line number within the file of the caller that emitted this message.
40 line int
41}
42
Serge Bazanski1bfa0c22020-10-14 16:45:07 +020043func (p *LeveledPayload) String() string {
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020044 // Same format as in glog:
45 // Lmmdd hh:mm:ss.uuuuuu threadid file:line]
46 // Except, threadid is (currently) always zero. In the future this field might be used for something different.
47
48 _, month, day := p.timestamp.Date()
49 hour, minute, second := p.timestamp.Clock()
50 nsec := p.timestamp.Nanosecond() / 1000
51
52 // TODO(q3k): rewrite this to printf-less code.
53 return fmt.Sprintf("%s%02d%02d %02d:%02d:%02d.%06d % 7d %s:%d] %s", p.severity, month, day, hour, minute, second,
54 nsec, 0, p.file, p.line, p.message)
55}
56
57// Message returns the inner message of this entry, ie. what was passed to the actual logging method.
Serge Bazanski1bfa0c22020-10-14 16:45:07 +020058func (p *LeveledPayload) Message() string { return p.message }
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020059
60// Timestamp returns the time at which this entry was logged.
Serge Bazanski1bfa0c22020-10-14 16:45:07 +020061func (p *LeveledPayload) Timestamp() time.Time { return p.timestamp }
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020062
63// Location returns a string in the form of file_name:line_number that shows the origin of the log entry in the
64// program source.
Serge Bazanski1bfa0c22020-10-14 16:45:07 +020065func (p *LeveledPayload) Location() string { return fmt.Sprintf("%s:%d", p.file, p.line) }
Serge Bazanski5faa2fc2020-09-07 14:09:30 +020066
67// Severity returns the Severity with which this entry was logged.
Serge Bazanski1bfa0c22020-10-14 16:45:07 +020068func (p *LeveledPayload) Severity() Severity { return p.severity }
Serge Bazanskib0272182020-11-02 18:39:44 +010069
70// Proto converts a LeveledPayload to protobuf format.
71func (p *LeveledPayload) Proto() *apb.LogEntry_Leveled {
72 return &apb.LogEntry_Leveled{
73 Message: p.Message(),
74 Timestamp: p.Timestamp().UnixNano(),
75 Severity: p.Severity().ToProto(),
76 Location: p.Location(),
77 }
78}
79
80// LeveledPayloadFromProto parses a protobuf message into the internal format.
81func LeveledPayloadFromProto(p *apb.LogEntry_Leveled) (*LeveledPayload, error) {
82 severity, err := SeverityFromProto(p.Severity)
83 if err != nil {
84 return nil, fmt.Errorf("could not convert severity: %w", err)
85 }
86 parts := strings.Split(p.Location, ":")
87 if len(parts) != 2 {
88 return nil, fmt.Errorf("invalid location, must be two :-delimited parts, is %d parts", len(parts))
89 }
90 file := parts[0]
91 line, err := strconv.Atoi(parts[1])
92 if err != nil {
93 return nil, fmt.Errorf("invalid location line number: %w", err)
94 }
95 return &LeveledPayload{
96 message: p.Message,
97 timestamp: time.Unix(0, p.Timestamp),
98 severity: severity,
99 file: file,
100 line: line,
101 }, nil
102}