1// Copyright (c) 2013 Couchbase, Inc.
2// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
3// except in compliance with the License. You may obtain a copy of the License at
4//   http://www.apache.org/licenses/LICENSE-2.0
5// Unless required by applicable law or agreed to in writing, software distributed under the
6// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
7// either express or implied. See the License for the specific language governing permissions
8// and limitations under the License.
9
10package log
11
12import (
13	"bytes"
14	"errors"
15	"fmt"
16	"io"
17	"log"
18	"os"
19	"path/filepath"
20	"time"
21)
22
23type LogLevel int
24
25var GOXDCR_COMPONENT_CODE = "GOXDCR."
26
27// starting point of valid log level values
28const BaseLogLevel = 10
29
30const (
31	LogLevelFatal LogLevel = iota + BaseLogLevel
32	LogLevelError
33	LogLevelWarn
34	LogLevelInfo
35	LogLevelDebug
36	LogLevelTrace
37)
38
39// log level on UI and rest api
40const (
41	LOG_LEVEL_FATAL_STR string = "Fatal"
42	LOG_LEVEL_ERROR_STR string = "Error"
43	LOG_LEVEL_WARN_STR  string = "Warn"
44	LOG_LEVEL_INFO_STR  string = "Info"
45	LOG_LEVEL_DEBUG_STR string = "Debug"
46	LOG_LEVEL_TRACE_STR string = "Trace"
47)
48
49// log level in log files
50const (
51	LOG_LEVEL_FATAL_LOG_STR string = "FATA"
52	LOG_LEVEL_ERROR_LOG_STR string = "ERRO"
53	LOG_LEVEL_WARN_LOG_STR  string = "WARN"
54	LOG_LEVEL_INFO_LOG_STR  string = "INFO"
55	LOG_LEVEL_DEBUG_LOG_STR string = "DEBU"
56	LOG_LEVEL_TRACE_LOG_STR string = "TRAC"
57)
58
59const (
60	XdcrLogFileName      = "xdcr.log"
61	XdcrTraceLogFileName = "xdcr_trace.log"
62	XdcrErrorLogFileName = "xdcr_errors.log"
63)
64
65// keep module separate from log.Logger so that we can control its formating
66type XdcrLogger struct {
67	logger *log.Logger
68	module string
69}
70
71type CommonLogger struct {
72	loggers map[LogLevel]*XdcrLogger
73	context *LoggerContext
74}
75
76type LoggerContext struct {
77	Log_writers map[LogLevel]*LogWriter
78	Log_level   LogLevel
79}
80
81func (lc *LoggerContext) SetLogLevel(logLevel LogLevel) {
82	logLevel2 := upgradeLogLevelIfNeeded(logLevel)
83	if lc.Log_level != logLevel2 {
84		// update log level only when necessary, e.g., when explicitly requested by user
85		lc.Log_level = logLevel2
86	}
87}
88
89type LogWriter struct {
90	writer io.Writer
91}
92
93// LogWriter implements io.Writer interface
94func (lw *LogWriter) Write(p []byte) (n int, err error) {
95	return lw.writer.Write(p)
96}
97
98func CopyCtx(ctx_to_copy *LoggerContext) *LoggerContext {
99	return &LoggerContext{Log_writers: ctx_to_copy.Log_writers,
100		Log_level: ctx_to_copy.Log_level}
101}
102
103var DefaultLoggerContext *LoggerContext
104
105// before logging paramters become available, direct all logging to stdout
106func init() {
107	logWriters := make(map[LogLevel]*LogWriter)
108	logWriter := &LogWriter{os.Stdout}
109	logWriters[LogLevelFatal] = logWriter
110	logWriters[LogLevelError] = logWriter
111	logWriters[LogLevelWarn] = logWriter
112	logWriters[LogLevelInfo] = logWriter
113	logWriters[LogLevelDebug] = logWriter
114	logWriters[LogLevelTrace] = logWriter
115
116	DefaultLoggerContext = &LoggerContext{
117		Log_writers: logWriters,
118		Log_level:   LogLevelInfo,
119	}
120}
121
122// re-initializes default logger context with runtime logging parameters
123// log entries will be written to log files after this point
124func Init(logFileDir string, maxLogFileSize, maxNumberOfLogFiles uint64) error {
125	// xdcr log file
126	xdcrLogFilePath := filepath.Join(logFileDir, XdcrLogFileName)
127	xdcrLogWriter, err := NewRotatingLogFileWriter(xdcrLogFilePath, maxLogFileSize, maxNumberOfLogFiles)
128	if err != nil {
129		return err
130	}
131	xdcrLogWriterWrapper := DefaultLoggerContext.Log_writers[LogLevelInfo]
132	xdcrLogWriterWrapper.writer = xdcrLogWriter
133
134	// xdcr trace log -- both debug level and trace level entries are written to this file
135	xdcrTraceFilePath := filepath.Join(logFileDir, XdcrTraceLogFileName)
136	xdcrTraceWriter, err := NewRotatingLogFileWriter(xdcrTraceFilePath, maxLogFileSize, maxNumberOfLogFiles)
137	if err != nil {
138		return err
139	}
140	xdcrDebugWriterWrapper := DefaultLoggerContext.Log_writers[LogLevelDebug]
141	xdcrDebugWriterWrapper.writer = xdcrTraceWriter
142	xdcrTraceWriterWrapper := DefaultLoggerContext.Log_writers[LogLevelTrace]
143	xdcrTraceWriterWrapper.writer = xdcrTraceWriter
144
145	// xdcr error log file
146	xdcrErrorFilePath := filepath.Join(logFileDir, XdcrErrorLogFileName)
147	xdcrErrorWriter, err := NewRotatingLogFileWriter(xdcrErrorFilePath, maxLogFileSize, maxNumberOfLogFiles)
148	if err != nil {
149		return err
150	}
151	xdcrErrorWriterWrapper := DefaultLoggerContext.Log_writers[LogLevelError]
152	xdcrErrorWriterWrapper.writer = xdcrErrorWriter
153
154	return nil
155}
156
157func NewLogger(module string, logger_context *LoggerContext) *CommonLogger {
158	context := DefaultLoggerContext
159	if logger_context != nil {
160		context = logger_context
161	}
162	loggers := make(map[LogLevel]*XdcrLogger)
163	for logLevel, logWriter := range context.Log_writers {
164		loggers[logLevel] = &XdcrLogger{log.New(logWriter, "", 0), module}
165	}
166	return &CommonLogger{loggers, context}
167}
168
169func (l *CommonLogger) logMsgf(level LogLevel, format string, v ...interface{}) {
170	if l.context.Log_level >= level {
171		l.loggers[level].logger.Printf(l.processCommonFields(level)+format, v...)
172	}
173}
174
175func (l *CommonLogger) logMsg(level LogLevel, msg string) {
176	if l.context.Log_level >= level {
177		l.loggers[level].logger.Println(l.processCommonFields(level) + msg)
178	}
179}
180
181func (l *CommonLogger) Fatalf(format string, v ...interface{}) {
182	l.logMsgf(LogLevelFatal, format, v...)
183}
184
185func (l *CommonLogger) Errorf(format string, v ...interface{}) {
186	l.logMsgf(LogLevelError, format, v...)
187}
188
189func (l *CommonLogger) Warnf(format string, v ...interface{}) {
190	l.logMsgf(LogLevelWarn, format, v...)
191}
192
193func (l *CommonLogger) Infof(format string, v ...interface{}) {
194	l.logMsgf(LogLevelInfo, format, v...)
195}
196
197func (l *CommonLogger) Debugf(format string, v ...interface{}) {
198	l.logMsgf(LogLevelDebug, format, v...)
199}
200
201func (l *CommonLogger) Tracef(format string, v ...interface{}) {
202	l.logMsgf(LogLevelTrace, format, v...)
203}
204
205func (l *CommonLogger) Fatal(msg string) {
206	l.logMsg(LogLevelFatal, msg)
207}
208
209func (l *CommonLogger) Error(msg string) {
210	l.logMsg(LogLevelError, msg)
211}
212
213func (l *CommonLogger) Warn(msg string) {
214	l.logMsg(LogLevelWarn, msg)
215}
216
217func (l *CommonLogger) Info(msg string) {
218	l.logMsg(LogLevelInfo, msg)
219}
220
221func (l *CommonLogger) Debug(msg string) {
222	l.logMsg(LogLevelDebug, msg)
223}
224
225func (l *CommonLogger) Trace(msg string) {
226	l.logMsgf(LogLevelTrace, msg)
227}
228
229func (l *CommonLogger) LoggerContext() *LoggerContext {
230	return l.context
231}
232
233func (l *CommonLogger) GetLogLevel() LogLevel {
234	return l.context.Log_level
235}
236
237func LogLevelFromStr(levelStr string) (LogLevel, error) {
238	var level LogLevel
239	switch levelStr {
240	case LOG_LEVEL_ERROR_STR:
241		level = LogLevelError
242	case LOG_LEVEL_WARN_STR:
243		level = LogLevelWarn
244	case LOG_LEVEL_INFO_STR:
245		level = LogLevelInfo
246	case LOG_LEVEL_DEBUG_STR:
247		level = LogLevelDebug
248	case LOG_LEVEL_TRACE_STR:
249		level = LogLevelTrace
250	default:
251		return -1, errors.New(fmt.Sprintf("%v is not a valid log level", levelStr))
252	}
253	return level, nil
254}
255
256func (level LogLevel) String() string {
257	level2 := upgradeLogLevelIfNeeded(level)
258	switch level2 {
259	case LogLevelFatal:
260		return LOG_LEVEL_FATAL_STR
261	case LogLevelError:
262		return LOG_LEVEL_ERROR_STR
263	case LogLevelWarn:
264		return LOG_LEVEL_WARN_STR
265	case LogLevelInfo:
266		return LOG_LEVEL_INFO_STR
267	case LogLevelDebug:
268		return LOG_LEVEL_DEBUG_STR
269	case LogLevelTrace:
270		return LOG_LEVEL_TRACE_STR
271	}
272	return ""
273}
274
275func (level LogLevel) LogString() string {
276	switch level {
277	case LogLevelFatal:
278		return LOG_LEVEL_FATAL_LOG_STR
279	case LogLevelError:
280		return LOG_LEVEL_ERROR_LOG_STR
281	case LogLevelWarn:
282		return LOG_LEVEL_WARN_LOG_STR
283	case LogLevelInfo:
284		return LOG_LEVEL_INFO_LOG_STR
285	case LogLevelDebug:
286		return LOG_LEVEL_DEBUG_LOG_STR
287	case LogLevelTrace:
288		return LOG_LEVEL_TRACE_LOG_STR
289	}
290	return ""
291}
292
293// compose fields that are common to all log messages
294// example log entry:
295// 2017-01-26T14:21:22.523-08:00 INFO GOXDCR.HttpServer: [xdcr:127.0.0.1:13000] starting ...
296func (l *CommonLogger) processCommonFields(level LogLevel) string {
297	var buffer bytes.Buffer
298	buffer.WriteString(FormatTimeWithMilliSecondPrecision(time.Now()))
299	buffer.WriteString(" ")
300	buffer.WriteString(level.LogString())
301	buffer.WriteString(" ")
302	buffer.WriteString(GOXDCR_COMPONENT_CODE)
303	buffer.WriteString(l.loggers[level].module)
304	buffer.WriteString(": ")
305	return buffer.String()
306}
307
308// example format: 2015-03-17T10:15:06.717-07:00
309func FormatTimeWithMilliSecondPrecision(origTime time.Time) string {
310	return origTime.Format("2006-01-02T15:04:05.000Z07:00")
311}
312
313func upgradeLogLevelIfNeeded(level LogLevel) LogLevel {
314	if level < BaseLogLevel {
315		// logLevel is before upgrade. convert it to new info log level
316		return LogLevelInfo
317	}
318
319	return level
320}
321