1// +build go1.13
2
3// Copyright (c) Microsoft Corporation. All rights reserved.
4// Licensed under the MIT License.
5
6package azcore
7
8import (
9	"fmt"
10	"os"
11	"time"
12)
13
14// LogClassification is used to group entries.  Each group can be toggled on or off.
15type LogClassification string
16
17const (
18	// LogRequest entries contain information about HTTP requests.
19	// This includes information like the URL, query parameters, and headers.
20	LogRequest LogClassification = "Request"
21
22	// LogResponse entries contain information about HTTP responses.
23	// This includes information like the HTTP status code, headers, and request URL.
24	LogResponse LogClassification = "Response"
25
26	// LogRetryPolicy entries contain information specific to the retry policy in use.
27	LogRetryPolicy LogClassification = "RetryPolicy"
28
29	// LogLongRunningOperation entries contain information specific to long-running operations.
30	// This includes information like polling location, operation state and sleep intervals.
31	LogLongRunningOperation LogClassification = "LongRunningOperation"
32)
33
34// Listener is the function signature invoked when writing log entries.
35// A Listener is required to perform its own synchronization if it's
36// expected to be called from multiple Go routines.
37type Listener func(LogClassification, string)
38
39// Logger controls which classifications to log and writing to the underlying log.
40type Logger struct {
41	cls []LogClassification
42	lst Listener
43}
44
45// SetClassifications is used to control which classifications are written to
46// the log.  By default all log classifications are written.
47func (l *Logger) SetClassifications(cls ...LogClassification) {
48	l.cls = cls
49}
50
51// SetListener will set the Logger to write to the specified Listener.
52func (l *Logger) SetListener(lst Listener) {
53	l.lst = lst
54}
55
56// Should returns true if the specified log classification should be written to the log.
57// By default all log classifications will be logged.  Call SetClassification() to limit
58// the log classifications for logging.
59// If no listener has been set this will return false.
60// Calling this method is useful when the message to log is computationally expensive
61// and you want to avoid the overhead if its log classification is not enabled.
62func (l *Logger) Should(cls LogClassification) bool {
63	if l.lst == nil {
64		return false
65	}
66	if l.cls == nil || len(l.cls) == 0 {
67		return true
68	}
69	for _, c := range l.cls {
70		if c == cls {
71			return true
72		}
73	}
74	return false
75}
76
77// Write invokes the underlying Listener with the specified classification and message.
78// If the classification shouldn't be logged or there is no listener then Write does nothing.
79func (l *Logger) Write(cls LogClassification, message string) {
80	if l.lst == nil || !l.Should(cls) {
81		return
82	}
83	l.lst(cls, message)
84}
85
86// Writef invokes the underlying Listener with the specified classification and formatted message.
87// If the classification shouldn't be logged or there is no listener then Writef does nothing.
88func (l *Logger) Writef(cls LogClassification, format string, a ...interface{}) {
89	if l.lst == nil || !l.Should(cls) {
90		return
91	}
92	l.lst(cls, fmt.Sprintf(format, a...))
93}
94
95// for testing purposes
96func (l *Logger) resetClassifications() {
97	l.cls = nil
98}
99
100var logger Logger
101
102// Log returns the process-wide logger.
103func Log() *Logger {
104	return &logger
105}
106
107func init() {
108	if cls := os.Getenv("AZURE_SDK_GO_LOGGING"); cls == "all" {
109		// cls could be enhanced to support a comma-delimited list of log classifications
110		logger.lst = func(cls LogClassification, msg string) {
111			// simple console logger, it writes to stderr in the following format:
112			// [time-stamp] Classification: message
113			fmt.Fprintf(os.Stderr, "[%s] %s: %s\n", time.Now().Format(time.StampMicro), cls, msg)
114		}
115	}
116}
117