| // 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 |
| } |