From 9543657b7020418cca3788c51cdfca145b226930 Mon Sep 17 00:00:00 2001 From: MarketDataApp Date: Mon, 19 Feb 2024 16:26:31 -0300 Subject: [PATCH] moved logging into main package --- .scripts/gomarkdoc.sh | 7 +- .scripts/process_markdown.py | 5 +- client.go | 17 +- helpers/logging/logging_test.go | 48 ---- helpers/logging/logging.go => logging.go | 308 +++++++++++++---------- logging_test.go | 45 +++- 6 files changed, 234 insertions(+), 196 deletions(-) delete mode 100644 helpers/logging/logging_test.go rename helpers/logging/logging.go => logging.go (62%) diff --git a/.scripts/gomarkdoc.sh b/.scripts/gomarkdoc.sh index 70f06a8..10e9510 100755 --- a/.scripts/gomarkdoc.sh +++ b/.scripts/gomarkdoc.sh @@ -139,6 +139,11 @@ process_group() { echo "Markdown processing and cleanup completed for $GROUP_NAME" } +: ' +MOVE PROCESS GROUPS HERE TO AVOID PROCESSING + +' + # Call process_group for each group name process_group "indices_candles" process_group "indices_quotes" @@ -155,7 +160,7 @@ process_group "options_quotes" process_group "options_strikes" process_group "options_chain" process_group "client" - +process_group "logging" # Add more calls to process_group with different group names as needed diff --git a/.scripts/process_markdown.py b/.scripts/process_markdown.py index 23cf917..2081dff 100755 --- a/.scripts/process_markdown.py +++ b/.scripts/process_markdown.py @@ -19,7 +19,9 @@ "https://www.marketdata.app/docs/api/options/strikes": {"title": "Strikes", "sidebar_position": 3}, "https://www.marketdata.app/docs/api/options/chain": {"title": "Option Chain", "sidebar_position": 4}, "https://www.marketdata.app/docs/api/options/quotes": {"title": "Quotes", "sidebar_position": 5}, - "https://www.marketdata.app/docs/sdk/go/client": {"title": "Client", "sidebar_position": 3}, + "https://www.marketdata.app/docs/sdk/go/client": {"title": "Client", "sidebar_position": 2}, + "https://www.marketdata.app/docs/sdk/go/logging": {"title": "Logging", "sidebar_position": 3}, + # Add more mappings as needed } @@ -574,6 +576,7 @@ def process_file(file_path): content = move_responses_to_top(content) #content = move_to_bottom(content, '', '') # content = colapse_bullet_points(content) replacements = { diff --git a/client.go b/client.go index 88f0a70..ba017cd 100644 --- a/client.go +++ b/client.go @@ -25,7 +25,6 @@ import ( "sync" "time" - "github.com/MarketDataApp/sdk-go/helpers/logging" "github.com/go-resty/resty/v2" _ "github.com/joho/godotenv/autoload" ) @@ -147,14 +146,14 @@ func (c *MarketDataClient) addLogFromRequestResponse(req *resty.Request, resp *r body := string(resp.Body()) // Create a new log entry with the gathered information. - logEntry := logging.AddToLog(GetLogs(), time.Now(), rayID, req.URL, rateLimitConsumed, delay, status, body, redactedHeaders, resHeaders) + logEntry := addToLog(GetLogs(), time.Now(), rayID, req.URL, rateLimitConsumed, delay, status, body, redactedHeaders, resHeaders) // If debug mode is enabled and the log entry is not nil, pretty print the log entry. if c.debug && logEntry != nil { logEntry.PrettyPrint() } // If the log entry is not nil, write it to the log. if logEntry != nil { - logEntry.WriteToLog(c.debug) + logEntry.writeToLog(c.debug) } return nil } @@ -604,14 +603,4 @@ func (c *MarketDataClient) Token(bearerToken string) error { } return fmt.Errorf("invalid token. received non-OK status: %s", resp.Status()) // Return error for non-successful response -} - -// GetLogs retrieves a pointer to the HttpRequestLogs instance, allowing access to the logs collected during HTTP requests. -// This method is primarily used for debugging and monitoring purposes, providing insights into the HTTP request lifecycle and any issues that may have occurred. -// -// # Returns -// -// - *logging.HttpRequestLogs: A pointer to the HttpRequestLogs instance containing logs of HTTP requests. -func GetLogs() *logging.HttpRequestLogs { - return logging.Logs -} +} \ No newline at end of file diff --git a/helpers/logging/logging_test.go b/helpers/logging/logging_test.go deleted file mode 100644 index 5bf85f0..0000000 --- a/helpers/logging/logging_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package logging - -import ( - "net/http" - "testing" - "time" -) - -func TestAddToLog(t *testing.T) { - // Initialize HttpRequestLogs - logs := &HttpRequestLogs{ - Logs: []HttpRequestLog{}, - } - - // Define a sample HttpRequestLog entry - timestamp := time.Now() - rayID := "sampleRayID" - request := "https://api.example.com/data" - rateLimitConsumed := 10 - delay := int64(100) - status := 200 - body := "{\"message\": \"success\"}" - reqHeaders := http.Header{"Content-Type": []string{"application/json"}} - resHeaders := http.Header{"Content-Type": []string{"application/json"}} - - // Add the log entry - logEntry := AddToLog(logs, timestamp, rayID, request, rateLimitConsumed, delay, status, body, reqHeaders, resHeaders) - - // Check if the log entry was added - if logEntry == nil { - t.Errorf("Log entry was not added") - } - - // Check if the log entry matches the input - if logEntry != nil { - if logEntry.RayID != rayID || logEntry.Request != request || logEntry.Response != body { - t.Errorf("Log entry does not match the input") - } - } else { - t.Errorf("Log entry is nil") - } - - // Check if the log entry is the last in the logs - lastLogEntry := logs.Logs[len(logs.Logs)-1] - if lastLogEntry.RayID != logEntry.RayID || lastLogEntry.Request != logEntry.Request || lastLogEntry.Response != logEntry.Response { - t.Errorf("Log entry is not the last entry in the logs") - } -} \ No newline at end of file diff --git a/helpers/logging/logging.go b/logging.go similarity index 62% rename from helpers/logging/logging.go rename to logging.go index dd9c63a..0219fbf 100644 --- a/helpers/logging/logging.go +++ b/logging.go @@ -1,4 +1,27 @@ -package logging +// Package client provides a collection of structs for logging. +// The Market Data Go SDK provides a [comprehensive logging framework] tailored for HTTP request and response tracking. +// It facilitates detailed logging of HTTP interactions, including request details, response data, error handling, and rate limit monitoring. +// The logging functionality is designed to support a wide range of market data types such as stocks, options, indices, and market status information, +// making it an essential tool for developers integrating market data into their applications. +// +// # Key Features +// +// - Detailed Logging: Captures and logs comprehensive details of HTTP requests and responses, including headers, status codes, and body content. +// - Error Handling: Distinguishes between client-side and server-side errors, logging them appropriately to aid in debugging and monitoring. +// - Rate Limit Management: Tracks and logs rate limit consumption for each request, helping to avoid rate limit breaches. +// - Debug Mode: Offers a debug mode that logs additional details for in-depth analysis during development and troubleshooting. +// - In-Memory Log Storage: Maintains the last log entries in memory with configurable limits on entries and memory usage, ensuring efficient log management. +// - Structured Logging: Utilizes the zap logging library for structured, high-performance logging. +// +// # Getting Started With Logging +// +// 1. Initialize the MarketDataClient with your API token. +// 2. Enable Debug mode for verbose logging during development. +// 3. Perform HTTP requests and utilize the logging features to monitor request details, responses, and rate limit usage. +// 4. Review the in-memory logs or structured log files to analyze HTTP interactions and troubleshoot issues. +// +// [comprehensive logging framework]: https://www.marketdata.app/docs/sdk/go/logging +package client import ( "encoding/json" @@ -22,43 +45,111 @@ var ( ) var ( - // SuccessLogger is a logger used for logging successful operations. - SuccessLogger *zap.Logger - // ClientErrorLogger is a logger used for logging client-side errors. - ClientErrorLogger *zap.Logger - // ServerErrorLogger is a logger used for logging server-side errors. - ServerErrorLogger *zap.Logger - // Logs holds a collection of HTTP request logs. - Logs *HttpRequestLogs - // MaxLogEntries defines the maximum number of log entries that will be stored in memory. - // Beyond this limit, older entries may be evicted or ignored. - MaxLogEntries = 100000 - // MemoryLimit specifies the maximum amount of memory (in bytes) that can be used for storing log entries. - // This helps in preventing excessive memory usage by log storage. - MemoryLimit int64 = 100 * 1048576 + // logs holds a collection of HTTP request logs. + logs *MarketDataLogs ) -// HttpRequestLogs represents a collection of HTTP request logs. +// MarketDataLogs represents a collection of HTTP request logs. // It provides methods to manipulate and retrieve log entries. // -// Public Methods: +// # Methods +// // - String() string: Returns a string representation of all HTTP request logs. // - PrintLatest(): Prints the latest HTTP request log entry. -type HttpRequestLogs struct { - Logs []HttpRequestLog +// - LatestString() string : Gets the HTTP body response of the last log entry in string format. +type MarketDataLogs struct { + // MaxLogEntries defines the maximum number of log entries that will be stored in memory. + // Beyond this limit, older entries may be evicted or ignored. + MaxEntries int + + // MemoryLimit specifies the maximum amount of memory (in bytes) that can be used for storing log entries. + // This helps in preventing excessive memory usage by in-memory log storage. + MemoryLimit int64 + + // SuccessLogger is a zap logger used for logging successful operations. + SuccessLogger *zap.Logger + + // ClientErrorLogger is a zap logger used for logging client-side errors. + ClientErrorLogger *zap.Logger + + // ServerErrorLogger is a zap logger used for logging server-side errors. + ServerErrorLogger *zap.Logger + + // Logs is the slice of LogEntry that contain all the request logs. + Logs []LogEntry +} + +// totalMemoryUsage calculates the total memory usage of all log entries. +// +// # Returns +// +// - An int64 representing the total memory usage of all log entries in bytes. +func (h *MarketDataLogs) totalMemoryUsage() int64 { + total := int64(0) + for _, log := range h.Logs { + total += log.memory + } + return total } -// HttpRequestLog represents a single HTTP request log entry. +// String returns a string representation of all HTTP request logs. +// +// # Returns +// +// - A string representing all log entries. +func (h *MarketDataLogs) String() string { + var sb strings.Builder + for _, log := range h.Logs { + sb.WriteString(log.String()) + sb.WriteString("\n") + } + return sb.String() +} + +// LatestString returns the response of the last log entry in the MarketDataLogs. +// +// This method checks if there are any logs present. If there are no logs, it returns a message indicating that no logs are available. +// If logs are present, it calculates the index of the last log entry, accesses it, and returns its response. +// +// # Returns +// +// - A string representing the response of the last log entry. If no logs are available, returns "No logs available". +func (h *MarketDataLogs) LatestString() string { + // Step 2: Check if there are no logs + if len(h.Logs) == 0 { + // Return an appropriate response for no logs + return "No logs available" + } + + // Step 3: Calculate the index of the last log entry and access it + lastLogIndex := len(h.Logs) - 1 + lastLog := h.Logs[lastLogIndex] + + // Step 4: Return the Response of the last log entry + return lastLog.Response +} + +// PrintLatest prints the latest HTTP request log entry. +func (h *MarketDataLogs) PrintLatest() { + if len(h.Logs) == 0 { + fmt.Println("No logs available") + } else { + fmt.Println(blue("Latest Log Entry:")) + h.Logs[len(h.Logs)-1].PrettyPrint() + } +} + +// LogEntry represents a single HTTP request log entry. // It includes detailed information about the request and response, such as headers, status code, and response body. // -// Public Methods: -// - WriteToLog(debug bool): Writes the log entry to the appropriate logger based on the HTTP response status. +// # Methods +// // - String() string: Returns a string representation of the HTTP request log entry. // - PrettyPrint(): Prints a formatted representation of the HTTP request log entry. -type HttpRequestLog struct { +type LogEntry struct { Timestamp time.Time - ReqHeaders http.Header // The Request Headers - ResHeaders http.Header // The Response Headers + RequestHeaders http.Header // The Request Headers + ResponseHeaders http.Header // The Response Headers RayID string // The Ray ID from the HTTP response Request string // The URL of the HTTP Request Status int // The status code of the response @@ -73,7 +164,7 @@ type HttpRequestLog struct { // # Parameters // // - debug: A boolean indicating whether to log as a debug message. -func (h HttpRequestLog) WriteToLog(debug bool) { +func (h LogEntry) writeToLog(debug bool) { var logger *zap.Logger var logMessage string @@ -91,14 +182,14 @@ func (h HttpRequestLog) WriteToLog(debug bool) { if h.Status >= 200 && h.Status < 300 { if debug { - logger = SuccessLogger + logger = logs.SuccessLogger logMessage = "Successful Request" } } else if h.Status >= 400 && h.Status < 500 { - logger = ClientErrorLogger + logger = logs.ClientErrorLogger logMessage = "Client Error" } else if h.Status >= 500 { - logger = ServerErrorLogger + logger = logs.ServerErrorLogger logMessage = "Server Error" } @@ -109,8 +200,8 @@ func (h HttpRequestLog) WriteToLog(debug bool) { zap.Int("ratelimit_consumed", h.RateLimitConsumed), zap.Int("response_code", h.Status), zap.Int64("delay_ms", h.Delay), - zap.Any("request_headers", h.ReqHeaders), - zap.Any("response_headers", h.ResHeaders), + zap.Any("request_headers", h.RequestHeaders), + zap.Any("response_headers", h.ResponseHeaders), zap.Any("response_body", responseBody), // Log the parsed JSON response or the original string ) @@ -122,23 +213,23 @@ func (h HttpRequestLog) WriteToLog(debug bool) { // # Returns // // - A string representing the log entry. -func (h HttpRequestLog) String() string { +func (h LogEntry) String() string { return fmt.Sprintf("Timestamp: %v, Status: %d, Request: %s, Request Headers: %s, RayID: %s, RateLimitConsumed: %d, Delay: %dms, Response Headers: %s, Response: %s", - h.Timestamp.Format("2006-01-02 15:04:05"), h.Status, h.Request, h.ReqHeaders, h.RayID, h.RateLimitConsumed, h.Delay, h.ResHeaders, h.Response) + h.Timestamp.Format("2006-01-02 15:04:05"), h.Status, h.Request, h.RequestHeaders, h.RayID, h.RateLimitConsumed, h.Delay, h.ResponseHeaders, h.Response) } // PrettyPrint prints a formatted representation of the HTTP request log entry. -func (h HttpRequestLog) PrettyPrint() { +func (h LogEntry) PrettyPrint() { fmt.Println(blue("Timestamp:"), h.Timestamp.Format("2006-01-02 15:04:05")) fmt.Println(blue("Request:"), h.Request) fmt.Println(blue("Request Headers:")) - h.printHeaders(h.ReqHeaders) + h.printHeaders(h.RequestHeaders) fmt.Println(blue("Status:"), h.Status) fmt.Println(blue("Ray ID:"), h.RayID) fmt.Println(blue("Rate Limit Consumed:"), h.RateLimitConsumed) fmt.Println(blue("Delay:"), fmt.Sprintf("%dms", h.Delay)) fmt.Println(blue("Response Headers:")) - h.printHeaders(h.ResHeaders) + h.printHeaders(h.ResponseHeaders) fmt.Println(blue("Response:"), h.Response) } @@ -147,7 +238,7 @@ func (h HttpRequestLog) PrettyPrint() { // # Parameters // // - headers: The HTTP headers to be printed. -func (h HttpRequestLog) printHeaders(headers http.Header) { +func (h LogEntry) printHeaders(headers http.Header) { keys := make([]string, 0, len(headers)) for name := range headers { keys = append(keys, name) @@ -169,7 +260,7 @@ func (h HttpRequestLog) printHeaders(headers http.Header) { // # Returns // // - An integer representing the memory usage of the log entry in bytes. -func (h HttpRequestLog) memoryUsage() int { +func (h LogEntry) memoryUsage() int { // Size of time.Time (24 bytes) timestampSize := 24 @@ -187,8 +278,8 @@ func (h HttpRequestLog) memoryUsage() int { memorySize := 8 // Size of http.Header - reqHeadersSize := h.headerSize(h.ReqHeaders) - resHeadersSize := h.headerSize(h.ResHeaders) + reqHeadersSize := h.headerSize(h.RequestHeaders) + resHeadersSize := h.headerSize(h.ResponseHeaders) totalSize := timestampSize + rayIDSize + requestSize + statusSize + rateLimitConsumedSize + delaySize + responseSize + memorySize + reqHeadersSize + resHeadersSize @@ -204,7 +295,7 @@ func (h HttpRequestLog) memoryUsage() int { // # Returns // // - An integer representing the memory usage of the headers in bytes. -func (h HttpRequestLog) headerSize(header http.Header) int { +func (h LogEntry) headerSize(header http.Header) int { size := 0 for key, values := range header { // Size of string: size of string header (16 bytes) + length of string @@ -217,27 +308,27 @@ func (h HttpRequestLog) headerSize(header http.Header) int { return size } -// NewHttpRequestLog creates a new instance of HttpRequestLog with the provided parameters. +// NewLogEntry creates a new instance of LogEntry with the provided parameters. // This function initializes the log entry with details of the HTTP request and response, // including timestamps, request and response headers, and other relevant information. // // # Parameters // -// - timestamp: The time at which the HTTP request was made. -// - rayID: A unique identifier for the request, typically used for tracing requests. -// - request: The raw HTTP request data as a string. -// - rateLimitConsumed: The amount of rate limit quota consumed by this request. -// - delay: The delay experienced during the processing of the request, in milliseconds. -// - status: The HTTP status code returned in the response. -// - body: The body of the HTTP response. -// - reqHeaders: The HTTP headers of the request. -// - resHeaders: The HTTP headers of the response. +// - time.Time: The time at which the HTTP request was made. +// - string: The rayID is a unique identifier for the request, typically used for tracing requests. +// - string: The raw HTTP request URL as a string. +// - int: rateLimitConsumed represents the amount of rate limit quota consumed by this request. +// - int64: The delay experienced during the processing of the request, in milliseconds. +// - int: The HTTP status code returned in the response. +// - string: The body of the HTTP response in string format. +// - http.Header: The HTTP headers of the request as http.Header. +// - http.Header: The HTTP headers of the response as http.Header. // // # Returns // -// - HttpRequestLog: An instance of HttpRequestLog populated with the provided parameters and calculated memory usage. -func NewHttpRequestLog(timestamp time.Time, rayID string, request string, rateLimitConsumed int, delay int64, status int, body string, reqHeaders http.Header, resHeaders http.Header) HttpRequestLog { - log := HttpRequestLog{ +// - LogEntry: An instance of LogEntry populated with the provided parameters and calculated memory usage. +func NewLogEntry(timestamp time.Time, rayID string, request string, rateLimitConsumed int, delay int64, status int, body string, reqHeaders http.Header, resHeaders http.Header) LogEntry { + log := LogEntry{ Timestamp: timestamp, Status: status, RayID: rayID, @@ -245,8 +336,8 @@ func NewHttpRequestLog(timestamp time.Time, rayID string, request string, rateLi Delay: delay, Request: request, Response: body, - ReqHeaders: reqHeaders, - ResHeaders: resHeaders, + RequestHeaders: reqHeaders, + ResponseHeaders: resHeaders, } log.memory = int64(log.memoryUsage()) @@ -254,74 +345,14 @@ func NewHttpRequestLog(timestamp time.Time, rayID string, request string, rateLi return log } -// totalMemoryUsage calculates the total memory usage of all log entries. -// -// # Returns +// addToLog adds a new HTTP request log entry to the MarketDataLogs. // -// - An int64 representing the total memory usage of all log entries in bytes. -func (h *HttpRequestLogs) totalMemoryUsage() int64 { - total := int64(0) - for _, log := range h.Logs { - total += log.memory - } - return total -} - -// String returns a string representation of all HTTP request logs. -// -// # Returns -// -// - A string representing all log entries. -func (h *HttpRequestLogs) String() string { - var sb strings.Builder - for _, log := range h.Logs { - sb.WriteString(log.String()) - sb.WriteString("\n") - } - return sb.String() -} - -// GetLastLogResponse returns the response of the last log entry in the HttpRequestLogs. -// -// This method checks if there are any logs present. If there are no logs, it returns a message indicating that no logs are available. -// If logs are present, it calculates the index of the last log entry, accesses it, and returns its response. -// -// # Returns -// -// - A string representing the response of the last log entry. If no logs are available, returns "No logs available". -func (h *HttpRequestLogs) GetLastLogResponse() string { - // Step 2: Check if there are no logs - if len(h.Logs) == 0 { - // Return an appropriate response for no logs - return "No logs available" - } - - // Step 3: Calculate the index of the last log entry and access it - lastLogIndex := len(h.Logs) - 1 - lastLog := h.Logs[lastLogIndex] - - // Step 4: Return the Response of the last log entry - return lastLog.Response -} - -// PrintLatest prints the latest HTTP request log entry. -func (h *HttpRequestLogs) PrintLatest() { - if len(h.Logs) == 0 { - fmt.Println("No logs available") - } else { - fmt.Println(blue("Latest Log Entry:")) - h.Logs[len(h.Logs)-1].PrettyPrint() - } -} - -// AddToLog adds a new HTTP request log entry to the HttpRequestLogs. -// -// This method creates a new HttpRequestLog entry based on the provided parameters and appends it to the HttpRequestLogs. +// This method creates a new LogEntry entry based on the provided parameters and appends it to the MarketDataLogs. // After adding a new log entry, it trims the log to ensure the total memory usage and the number of log entries are below their limits. // // # Parameters // -// - h *HttpRequestLogs: A pointer to the HttpRequestLogs to which the new log entry will be added. +// - h *MarketDataLogs: A pointer to the MarketDataLogs to which the new log entry will be added. // - timestamp time.Time: The timestamp of the HTTP request. // - rayID string: The unique identifier for the request. // - request string: The URL of the HTTP request. @@ -334,15 +365,15 @@ func (h *HttpRequestLogs) PrintLatest() { // // # Returns // -// - *HttpRequestLog: A pointer to the newly added HttpRequestLog entry. Returns nil if the log entry is not added. -func AddToLog(h *HttpRequestLogs, timestamp time.Time, rayID string, request string, rateLimitConsumed int, delay int64, status int, body string, reqHeaders http.Header, resHeaders http.Header) *HttpRequestLog { +// - *LogEntry: A pointer to the newly added LogEntry entry. Returns nil if the log entry is not added. +func addToLog(h *MarketDataLogs, timestamp time.Time, rayID string, request string, rateLimitConsumed int, delay int64, status int, body string, reqHeaders http.Header, resHeaders http.Header) *LogEntry { if request == "https://api.marketdata.app/user/" { // If the URL starts with https://api.marketdata.app/user/ do not add it to the log. // Just return without doing anything in this case. return nil } - log := NewHttpRequestLog(timestamp, rayID, request, rateLimitConsumed, delay, status, body, reqHeaders, resHeaders) + log := NewLogEntry(timestamp, rayID, request, rateLimitConsumed, delay, status, body, reqHeaders, resHeaders) h.Logs = append(h.Logs, log) @@ -353,11 +384,11 @@ func AddToLog(h *HttpRequestLogs, timestamp time.Time, rayID string, request str return &h.Logs[len(h.Logs)-1] } -// trimLog trims the HttpRequestLogs to ensure that the total memory usage and the number of log entries do not exceed their respective limits. +// trimLog trims the MarketDataLogs to ensure that the total memory usage and the number of log entries do not exceed their respective limits. // It iteratively removes the oldest log entry until the memory usage is below the MemoryLimit and the number of entries is less than or equal to MaxLogEntries. -func (h *HttpRequestLogs) trimLog() { +func (h *MarketDataLogs) trimLog() { // While the total memory usage is above the limit or there are too many log entries, remove the oldest log entry - for (h.totalMemoryUsage() > MemoryLimit || len(h.Logs) > MaxLogEntries) && len(h.Logs) > 0 { + for (h.totalMemoryUsage() > logs.MemoryLimit || len(h.Logs) > logs.MaxEntries) && len(h.Logs) > 0 { h.Logs = h.Logs[1:] } } @@ -365,7 +396,7 @@ func (h *HttpRequestLogs) trimLog() { // init initializes the logging system for the application. // // This function performs the following operations: -// - Initializes the Logs variable with an empty HttpRequestLogs. +// - Initializes the Logs variable with an empty MarketDataLogs. // - Checks if the logs directory exists, and creates it if it does not. // - Opens or creates the success, client error, and server error log files. // - Sets up a zapcore.Core for each log file to enable structured logging. @@ -376,8 +407,10 @@ func (h *HttpRequestLogs) trimLog() { // The time encoding for log entries is set to ISO8601 format. func init() { // Initialize the Logs variable - Logs = &HttpRequestLogs{ - Logs: []HttpRequestLog{}, + logs = &MarketDataLogs{ + Logs: []LogEntry{}, + MemoryLimit: 10 * 1048576, + MaxEntries: 100000, } // Create the logs directory if it does not exist @@ -393,16 +426,19 @@ func init() { if err != nil { log.Fatalf("Failed to open success log file: %v", err) } + defer successLogFile.Close() // Ensure the file is closed when no longer needed clientErrorLogFile, err := os.OpenFile("logs/client_error.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { log.Fatalf("Failed to open client error log file: %v", err) } + defer clientErrorLogFile.Close() // Ensure the file is closed when no longer needed serverErrorLogFile, err := os.OpenFile("logs/server_error.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { log.Fatalf("Failed to open server error log file: %v", err) } + defer serverErrorLogFile.Close() // Ensure the file is closed when no longer needed // Create a zapcore.Core that writes to the log files encoderConfig := zap.NewProductionEncoderConfig() @@ -427,7 +463,17 @@ func init() { ) // Create a zap.Logger from the Core - SuccessLogger = zap.New(successCore) - ClientErrorLogger = zap.New(clientErrorCore) - ServerErrorLogger = zap.New(serverErrorCore) + logs.SuccessLogger = zap.New(successCore) + logs.ClientErrorLogger = zap.New(clientErrorCore) + logs.ServerErrorLogger = zap.New(serverErrorCore) +} + +// GetLogs retrieves a pointer to the MarketDataLogs instance, allowing access to the logs collected during HTTP requests. +// This method is primarily used for debugging and monitoring purposes, providing insights into the HTTP request lifecycle and any issues that may have occurred. +// +// # Returns +// +// - *MarketDataLogs: A pointer to the MarketDataLogs instance containing logs of HTTP requests. +func GetLogs() *MarketDataLogs { + return logs } diff --git a/logging_test.go b/logging_test.go index 10689b8..6f2b33c 100644 --- a/logging_test.go +++ b/logging_test.go @@ -3,7 +3,9 @@ package client import ( "fmt" "log" + "net/http" "testing" + "time" ) func TestLogging(t *testing.T) { @@ -23,9 +25,50 @@ func TestLogging(t *testing.T) { } scBodyString := sc.String() - lastLogResponse := GetLogs().GetLastLogResponse() + lastLogResponse := GetLogs().LatestString() if scBodyString != lastLogResponse { t.Errorf("Expected last log response to be %v, got %v instead", scBodyString, lastLogResponse) } } + +func TestAddToLog(t *testing.T) { + // Initialize MarketDataLogs + logs := &MarketDataLogs{ + Logs: []LogEntry{}, + } + + // Define a sample LogEntry entry + timestamp := time.Now() + rayID := "sampleRayID" + request := "https://api.example.com/data" + rateLimitConsumed := 10 + delay := int64(100) + status := 200 + body := "{\"message\": \"success\"}" + reqHeaders := http.Header{"Content-Type": []string{"application/json"}} + resHeaders := http.Header{"Content-Type": []string{"application/json"}} + + // Add the log entry + logEntry := addToLog(logs, timestamp, rayID, request, rateLimitConsumed, delay, status, body, reqHeaders, resHeaders) + + // Check if the log entry was added + if logEntry == nil { + t.Errorf("Log entry was not added") + } + + // Check if the log entry matches the input + if logEntry != nil { + if logEntry.RayID != rayID || logEntry.Request != request || logEntry.Response != body { + t.Errorf("Log entry does not match the input") + } + } else { + t.Errorf("Log entry is nil") + } + + // Check if the log entry is the last in the logs + lastLogEntry := logs.Logs[len(logs.Logs)-1] + if lastLogEntry.RayID != logEntry.RayID || lastLogEntry.Request != logEntry.Request || lastLogEntry.Response != logEntry.Response { + t.Errorf("Log entry is not the last entry in the logs") + } +}