blob: 69df62de810a0eb5c8ec653304b4e064d6985523 [file] [log] [blame]
// Copyright (C) 2016 The Android Open Source Project
//
// 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 log
import "io"
// Logger contains the tracking information while generating a filtered logging message.
// They are immutable, passed by value and allocated on the stack.
// You construct one with the At function, chain WithValue calls to build the context, and finish with a Log call.
// You can build a partially complete builder and use it to log many times with similar messages, this is very useful
// if you want logging statements in a loop where even the minimal cost of initializing the Builder is too much.
type Logger struct {
Context Context
Filter Filter
}
// At constructs a new Logger from the supplied context at the specified severity level.
// It applies the prefilter, and returns an inactive logger if the severity level is not active.
func (ctx logContext) At(severity Severity) Logger {
pre := GetPreFilter(ctx)
if !pre(severity) {
return Logger{}
}
return Logger{
Context: ctx.Severity(severity),
Filter: GetFilter(ctx),
}
// TODO: should this apply the main filter as well in here?
}
// Active returns true if the logger is not suppressing log messages.
func (l Logger) Active() bool {
return l.Context != nil
}
// updates the logger in place, only use when you have already copied the logger and checked it is active
func (l *Logger) update(key string, value interface{}) {
if l.Filter(l.Context, key, value) {
l.Context = l.Context.WithValue(key, value)
} else {
l.Context = nil
}
}
// like update, but does not add key to the chain, used for internal values with well known keys to avoid the
// cost of updating the key chain for the common cases. Basically anything that ends up as a field of Record
// outside of the Properties array uses this.
func (l *Logger) updateUnkeyed(key string, value interface{}) {
if l.Filter(l.Context, key, value) {
l.Context = setValue(l.Context, key, value)
} else {
l.Context = nil
}
}
func (l Logger) Writer() io.WriteCloser {
return &logAdapter{logger: l}
}
type logAdapter struct {
logger Logger
remains string
}
func (a *logAdapter) Write(p []byte) (n int, err error) {
s := string(p)
start := 0
for i, c := range s {
if c == '\n' {
fragment := s[start:i]
start = i + 1
if len(a.remains) > 0 {
fragment = a.remains + fragment
a.remains = ""
}
a.logger.Log(fragment)
}
}
fragment := s[start:]
if len(a.remains) > 0 {
fragment = a.remains + fragment
}
a.remains = fragment
return len(p), nil
}
func (a logAdapter) Close() error {
if len(a.remains) > 0 {
a.logger.Log(a.remains)
}
a.remains = ""
return nil
}