log.go raw
1 // Copyright (c) 2016, 2018, 2025, Oracle and/or its affiliates. All rights reserved.
2 // This software is dual-licensed to you under the Universal Permissive License (UPL) 1.0 as shown at https://oss.oracle.com/licenses/upl or Apache License 2.0 as shown at http://www.apache.org/licenses/LICENSE-2.0. You may choose either license.
3
4 package common
5
6 import (
7 "fmt"
8 "io"
9 "io/ioutil"
10 "log"
11 "os"
12 "strings"
13 "sync"
14 "time"
15 )
16
17 // sdkLogger an interface for logging in the SDK
18 type sdkLogger interface {
19 //LogLevel returns the log level of sdkLogger
20 LogLevel() int
21
22 //Log logs v with the provided format if the current log level is loglevel
23 Log(logLevel int, format string, v ...interface{}) error
24 }
25
26 // noLogging no logging messages
27 const noLogging = 0
28
29 // infoLogging minimal logging messages
30 const infoLogging = 1
31
32 // debugLogging some logging messages
33 const debugLogging = 2
34
35 // verboseLogging all logging messages
36 const verboseLogging = 3
37
38 // DefaultSDKLogger the default implementation of the sdkLogger
39 type DefaultSDKLogger struct {
40 currentLoggingLevel int
41 verboseLogger *log.Logger
42 debugLogger *log.Logger
43 infoLogger *log.Logger
44 nullLogger *log.Logger
45 }
46
47 // defaultLogger is the defaultLogger in the SDK
48 var defaultLogger sdkLogger
49 var loggerLock sync.Mutex
50 var file *os.File
51
52 // initializes the SDK defaultLogger as a defaultLogger
53 func init() {
54 l, _ := NewSDKLogger()
55 SetSDKLogger(l)
56 }
57
58 // SetSDKLogger sets the logger used by the sdk
59 func SetSDKLogger(logger sdkLogger) {
60 loggerLock.Lock()
61 defaultLogger = logger
62 loggerLock.Unlock()
63 }
64
65 // NewSDKLogger creates a defaultSDKLogger
66 // Debug logging is turned on/off by the presence of the environment variable "OCI_GO_SDK_DEBUG"
67 // The value of the "OCI_GO_SDK_DEBUG" environment variable controls the logging level.
68 // "null" outputs no log messages
69 // "i" or "info" outputs minimal log messages
70 // "d" or "debug" outputs some logs messages
71 // "v" or "verbose" outputs all logs messages, including body of requests
72 func NewSDKLogger() (DefaultSDKLogger, error) {
73 logger := DefaultSDKLogger{}
74
75 logger.currentLoggingLevel = noLogging
76 logger.verboseLogger = log.New(os.Stderr, "VERBOSE ", log.Ldate|log.Lmicroseconds|log.Lshortfile)
77 logger.debugLogger = log.New(os.Stderr, "DEBUG ", log.Ldate|log.Lmicroseconds|log.Lshortfile)
78 logger.infoLogger = log.New(os.Stderr, "INFO ", log.Ldate|log.Lmicroseconds|log.Lshortfile)
79 logger.nullLogger = log.New(ioutil.Discard, "", log.Ldate|log.Lmicroseconds|log.Lshortfile)
80
81 configured, isLogEnabled := os.LookupEnv("OCI_GO_SDK_DEBUG")
82
83 // If env variable not present turn logging off
84 if !isLogEnabled {
85 logger.currentLoggingLevel = noLogging
86 } else {
87 logOutputModeConfig(logger)
88
89 switch strings.ToLower(configured) {
90 case "null":
91 logger.currentLoggingLevel = noLogging
92 break
93 case "i", "info":
94 logger.currentLoggingLevel = infoLogging
95 break
96 case "d", "debug":
97 logger.currentLoggingLevel = debugLogging
98 break
99 //1 here for backwards compatibility
100 case "v", "verbose", "1":
101 logger.currentLoggingLevel = verboseLogging
102 break
103 default:
104 logger.currentLoggingLevel = infoLogging
105 }
106 logger.infoLogger.Println("logger level set to: ", logger.currentLoggingLevel)
107 }
108
109 return logger, nil
110 }
111
112 func (l DefaultSDKLogger) getLoggerForLevel(logLevel int) *log.Logger {
113 if logLevel > l.currentLoggingLevel {
114 return l.nullLogger
115 }
116
117 switch logLevel {
118 case noLogging:
119 return l.nullLogger
120 case infoLogging:
121 return l.infoLogger
122 case debugLogging:
123 return l.debugLogger
124 case verboseLogging:
125 return l.verboseLogger
126 default:
127 return l.nullLogger
128 }
129 }
130
131 // Set SDK Log output mode
132 // Output mode is switched based on environment variable "OCI_GO_SDK_LOG_OUPUT_MODE"
133 // "file" outputs log to a specific file
134 // "combine" outputs log to both stderr and specific file
135 // other unsupported value outputs log to stderr
136 // output file can be set via environment variable "OCI_GO_SDK_LOG_FILE"
137 // if this environment variable is not set, a default log file will be created under project root path
138 func logOutputModeConfig(logger DefaultSDKLogger) {
139 logMode, isLogOutputModeEnabled := os.LookupEnv("OCI_GO_SDK_LOG_OUTPUT_MODE")
140 if !isLogOutputModeEnabled {
141 return
142 }
143 fileName, isLogFileNameProvided := os.LookupEnv("OCI_GO_SDK_LOG_FILE")
144 if !isLogFileNameProvided {
145 fileName = fmt.Sprintf("logging_%v%s", time.Now().Unix(), ".log")
146 }
147
148 switch strings.ToLower(logMode) {
149 case "file", "f":
150 file = openLogOutputFile(logger, fileName)
151 logger.infoLogger.SetOutput(file)
152 logger.debugLogger.SetOutput(file)
153 logger.verboseLogger.SetOutput(file)
154 break
155 case "combine", "c":
156 file = openLogOutputFile(logger, fileName)
157 wrt := io.MultiWriter(os.Stderr, file)
158
159 logger.infoLogger.SetOutput(wrt)
160 logger.debugLogger.SetOutput(wrt)
161 logger.verboseLogger.SetOutput(wrt)
162 break
163 }
164 }
165
166 func openLogOutputFile(logger DefaultSDKLogger, fileName string) *os.File {
167 file, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
168 if err != nil {
169 logger.verboseLogger.Fatal(err)
170 }
171 return file
172 }
173
174 // CloseLogFile close the logging file and return error
175 func CloseLogFile() error {
176 return file.Close()
177 }
178
179 // LogLevel returns the current debug level
180 func (l DefaultSDKLogger) LogLevel() int {
181 return l.currentLoggingLevel
182 }
183
184 // Log logs v with the provided format if the current log level is loglevel
185 func (l DefaultSDKLogger) Log(logLevel int, format string, v ...interface{}) error {
186 logger := l.getLoggerForLevel(logLevel)
187 logger.Output(4, fmt.Sprintf(format, v...))
188 return nil
189 }
190
191 // Logln logs v appending a new line at the end
192 // Deprecated
193 func Logln(v ...interface{}) {
194 defaultLogger.Log(infoLogging, "%v\n", v...)
195 }
196
197 // Logf logs v with the provided format
198 func Logf(format string, v ...interface{}) {
199 defaultLogger.Log(infoLogging, format, v...)
200 }
201
202 // Debugf logs v with the provided format if debug mode is set
203 func Debugf(format string, v ...interface{}) {
204 defaultLogger.Log(debugLogging, format, v...)
205 }
206
207 // Debug logs v if debug mode is set
208 func Debug(v ...interface{}) {
209 m := fmt.Sprint(v...)
210 defaultLogger.Log(debugLogging, "%s", m)
211 }
212
213 // Debugln logs v appending a new line if debug mode is set
214 func Debugln(v ...interface{}) {
215 m := fmt.Sprint(v...)
216 defaultLogger.Log(debugLogging, "%s\n", m)
217 }
218
219 // IfDebug executes closure if debug is enabled
220 func IfDebug(fn func()) {
221 if defaultLogger.LogLevel() >= debugLogging {
222 fn()
223 }
224 }
225
226 // IfInfo executes closure if info is enabled
227 func IfInfo(fn func()) {
228 if defaultLogger.LogLevel() >= infoLogging {
229 fn()
230 }
231 }
232