Skip to content

Commit

Permalink
Updated structured logging logic, readme docs to mention new package
Browse files Browse the repository at this point in the history
  • Loading branch information
Muhammad Haseeb committed May 15, 2024
1 parent 122cdbc commit 444e2cf
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 110 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@ The https package provides logic related to HTTP requests, including building an
| [`Interceptors`](https/interceptors.go) | Provides handling to intercept requests. |
| [`Retryer`](https/retryer.go) | Provides handling to automatically retry for failed requests. |

### Logger
The logger package provides logic related to logging. It offers the Facade Design Pattern for configuring the Logger and SDK Logger. Additionally, it provides the LoggerConfiguration to customize logging behavior.

| File Name | Description |
|-----------------------------------------------------|----------------------------------------------------------------------------------------|
| [`Console Logger`](logger/defaultLogger.go) | Provides default implementation of [`Logger Interface`](logger/defaultLogger.go) to log messages. |
| [`Level`](logger/level.go) | Provides constants for log level like Level_ERROR, Level_INFO, etc. |
| [`Logger Configuration`](logger/loggerConfiguration.go) | Provides logging configurations for the Sdk Logger. |
| [`Message Logger Configuration`](logger/messageLoggerConfiguration.go) | Provides response logging configurations for the Sdk Logger. |
| [`Request Logger Configuration`](logger/requestLoggerConfiguration.go) | Provides request logging configurations for the Sdk Logger. |
| [`Sdk Logger`](logger/sdkLogger.go) | Provides default and null implementation of [` Sdk Logger Interface`](logger/sdkLogger.go) to log API requests and responses.

### Test Helper
Package testHelper provides helper functions for testing purposes.

Expand Down
22 changes: 18 additions & 4 deletions logger/defaultLogger.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package logger

import (
"encoding/json"
"fmt"
"regexp"
)

// LoggerInterface represents an interface for a generic logger.
Expand All @@ -19,7 +19,21 @@ func (c ConsoleLogger) Log(level Level, message string, params map[string]any) {
fmt.Println(level, ": ", _formatMessage(message, params))
}

func _formatMessage(message string, params map[string]any) string {
byt, _ := json.Marshal(params)
return message + " " + string(byt)
func _formatMessage(msg string, obj map[string]interface{}) string {
regex := regexp.MustCompile(`\%{([^}]+)}`)

formattedMsg := regex.ReplaceAllStringFunc(msg, func(match string) string {
key := match[2 : len(match)-1]
if value, ok := obj[key]; ok {
switch v := value.(type) {
case string:
return v
default:
return fmt.Sprintf("%v", v)
}
}
return match
})

return formattedMsg
}
File renamed without changes.
File renamed without changes.
14 changes: 0 additions & 14 deletions logger/nullSdkLogger.go

This file was deleted.

File renamed without changes.
136 changes: 44 additions & 92 deletions logger/sdkLogger.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package logger

import (
"fmt"
"net/http"
"strings"
)
Expand All @@ -14,6 +13,15 @@ type SdkLoggerInterface interface {
LogResponse(response *http.Response)
}

// NullSdkLogger represents implementation for SdkLoggerInterface, implementing methods to log HTTP requests and responses.
type NullSdkLogger struct{}

// LogRequest request Logs an HTTP request.
func (a NullSdkLogger) LogRequest(_request *http.Request) {}

// LogResponse Logs an HTTP response.
func (a NullSdkLogger) LogResponse(_response *http.Response) {}

// SdkLogger represents implementation for SdkLoggerInterface, providing methods to log HTTP requests and responses.
type SdkLogger struct {
loggingOptions LoggerConfiguration
Expand Down Expand Up @@ -41,7 +49,7 @@ func (a *SdkLogger) LogRequest(request *http.Request) {

a.logger.Log(
logLevel,
fmt.Sprintf("Request %v %v %v", request.Method, url, contentTypeHeader),
"Request %{method} %{url} %{contentType}",
map[string]any{
"method": request.Method,
"url": url,
Expand All @@ -53,116 +61,58 @@ func (a *SdkLogger) LogRequest(request *http.Request) {

// LogResponse Logs an HTTP response.
func (a *SdkLogger) LogResponse(response *http.Response) {
var logLevel = a.loggingOptions.level
var contentTypeHeader = a._getContentType(response.Header)
var contentLengthHeader = a._getContentLength(response.Header)

level := a.loggingOptions.level
a.logger.Log(
logLevel,
fmt.Sprintf("Response %v %v %v", response.StatusCode, contentLengthHeader, contentTypeHeader),
level,
"Response %{statusCode} %{contentLength} %{contentType}",
map[string]any{
"statusCode": response.StatusCode,
"contentLength": contentLengthHeader,
"contentType": contentTypeHeader,
"contentLength": a._getContentLength(response.Header),
"contentType": a._getContentType(response.Header),
},
)

a._applyLogResponseOptions(logLevel, response)
a._applyLogResponseOptions(level, response)
}

func (a *SdkLogger) _applyLogRequestOptions(level Level, request *http.Request) {
a._applyLogRequestHeaders(
level,
request,
a.loggingOptions.request,
)

a._applyLogRequestBody(level, request, a.loggingOptions.request)
}

func (a *SdkLogger) _applyLogRequestHeaders(
level Level,
request *http.Request,
logRequest RequestLoggerConfiguration) {

logHeaders := logRequest.headers
headersToInclude := logRequest.includeHeaders
headersToExclude := logRequest.excludeHeaders
headersToWhitelist := logRequest.whitelistHeaders

if logHeaders {
var headersToLog = a._extractHeadersToLog(
headersToInclude,
headersToExclude,
headersToWhitelist,
request.Header,
)

a.logger.Log(
level,
fmt.Sprintf("Request headers %v", headersToLog),
map[string]any{"headers": headersToLog},
logOp := a.loggingOptions.request
if logOp.headers {
a.logger.Log(level, "Request headers %{headers}",
map[string]any{"headers": a._extractHeadersToLog(
logOp.includeHeaders,
logOp.excludeHeaders,
logOp.whitelistHeaders,
request.Header,
)},
)
}
}

func (a *SdkLogger) _applyLogRequestBody(
level Level,
request *http.Request,
logRequest RequestLoggerConfiguration) {

if logRequest.body {
a.logger.Log(level, fmt.Sprintf("Request body %v", request.Body),
if logOp.body {
a.logger.Log(level, "Request body %{body}",
map[string]any{"body": request.Body},
)
}
}

func (a *SdkLogger) _applyLogResponseOptions(level Level, response *http.Response) {
a._applyLogResponseHeaders(
level,
response,
a.loggingOptions.response,
)

a._applyLogResponseBody(
level,
response,
a.loggingOptions.response,
)
}

func (a *SdkLogger) _applyLogResponseHeaders(
level Level,
response *http.Response,
logResponse MessageLoggerConfiguration) {

logHeaders := logResponse.headers
headersToInclude := logResponse.includeHeaders
headersToExclude := logResponse.excludeHeaders
headersToWhitelist := logResponse.whitelistHeaders

if logHeaders {
var headersToLog = a._extractHeadersToLog(
headersToInclude,
headersToExclude,
headersToWhitelist,
response.Header,
)

a.logger.Log(level, fmt.Sprintf("Response headers %v", headersToLog),
map[string]any{"headers": headersToLog},
logOp := a.loggingOptions.response
if logOp.headers {
a.logger.Log(level, "Response headers %{headers}",
map[string]any{"headers": a._extractHeadersToLog(
logOp.includeHeaders,
logOp.excludeHeaders,
logOp.whitelistHeaders,
response.Header,
)},
)
}
}

func (a *SdkLogger) _applyLogResponseBody(
level Level,
response *http.Response,
logResponse MessageLoggerConfiguration) {

if logResponse.body {
a.logger.Log(level, fmt.Sprintf("Response body %v", response.Body),
if logOp.body {
a.logger.Log(level, "Response body %{body}",
map[string]any{"body": response.Body},
)
}
Expand Down Expand Up @@ -228,9 +178,11 @@ func (a *SdkLogger) _includeHeadersToLog(
headersToInclude []string) http.Header {
// Filter headers based on the keys specified in includeHeaders
for _, name := range headersToInclude {
val, ok := headers[name]
if len(val) > 0 && ok {
filteredHeaders[name] = val
nameLower := strings.ToLower(name)
for headerKey, headerVal := range headers {
if strings.ToLower(headerKey) == nameLower {
filteredHeaders[headerKey] = headerVal
}
}
}
return filteredHeaders
Expand All @@ -252,7 +204,7 @@ func (a *SdkLogger) _excludeHeadersToLog(

func _contains(key string, slice []string) bool {
for _, name := range slice {
if name == key {
if strings.EqualFold(name, key) {
return true
}
}
Expand Down

0 comments on commit 444e2cf

Please sign in to comment.