diff --git a/essential/visible/service/helper/log/log.go b/essential/visible/service/helper/log/log.go index e69de29..d99d8b4 100644 --- a/essential/visible/service/helper/log/log.go +++ b/essential/visible/service/helper/log/log.go @@ -0,0 +1,225 @@ +/* +|-------------------------------------------------------------------------- +| Description +|-------------------------------------------------------------------------- +| +| Name: +| - Log +| +| Purpose: +| - Provide a framework-level logging utility that records structured +| plaintext log entries to a persistent filesystem location using a +| deterministic and platform-aware storage strategy. +| +|-------------------------------------------------------------------------- +*/ + +/* +|-------------------------------------------------------------------------- +| Instruction +|-------------------------------------------------------------------------- +| +| Guideline: +| - Use to write standardized log messages from framework services and +| software features. +| - Provide a logical source identifier using the SERVICENAME/UTILITYNAME +| or SERVICENAME/FEATURENAME format. +| - Provide the human-readable log message as plain, formatted, or any +| UTF-8 string. +| - Log entries are stored in the platform-specific persistent log +| directory using the software name as the directory name. +| - Log entries are appended as complete UTF-8 plaintext lines without +| overwriting existing content. +| +| Example: +| - ObrimLog( +| "SERVICENAME/UTILITYNAME", +| "Utility operation completed", +| ) +| - ObrimLog( +| "SERVICENAME/FEATURENAME", +| "Feature execution started", +| ) +| +|-------------------------------------------------------------------------- +*/ + +/* +|-------------------------------------------------------------------------- +| Credit +|-------------------------------------------------------------------------- +| +| Contributor: +| - Rajon Ahmed +| - Blockonite +| +|-------------------------------------------------------------------------- +*/ + +package log + +import ( + "os" + "path/filepath" + "runtime" + "strings" + "time" +) + +// obrimLogSoftwareName identifies the software owning the log directory. +const obrimLogSoftwareName = "software" + +// ObrimLog writes a standardized log message. +func ObrimLog(label, message string) { + obrimLogLabel, obrimLogMessage, obrimLogValid := obrimLogValidateInput(label, message) + if !obrimLogValid { + return + } + + obrimLogName := obrimLogResolveName() + if obrimLogName == "" { + return + } + + obrimLogDirectory, obrimLogDirectoryValid := obrimLogResolveDirectory(obrimLogName) + if !obrimLogDirectoryValid { + return + } + + obrimLogFile, obrimLogFileValid := obrimLogResolveFile(obrimLogDirectory) + if !obrimLogFileValid { + return + } + + obrimLogTimestamp := obrimLogGetTimestamp() + obrimLogEntry := obrimLogBuildEntry(obrimLogTimestamp, obrimLogLabel, obrimLogMessage) + + obrimLogWriteEntry(obrimLogFile, obrimLogEntry) +} + +// obrimLogValidateInput normalizes and validates the log input. +func obrimLogValidateInput(label, message string) (string, string, bool) { + label = strings.TrimSpace(label) + message = strings.ReplaceAll(message, "\r\n", "\n") + message = strings.ReplaceAll(message, "\r", "\n") + message = strings.ReplaceAll(message, "\n", " ") + + if label == "" || message == "" { + return "", "", false + } + + return label, message, true +} + +// obrimLogResolveName retrieves the software name from the local constant. +func obrimLogResolveName() string { + return strings.TrimSpace(obrimLogSoftwareName) +} + +// obrimLogResolveDirectory resolves and creates the platform-specific log directory. +func obrimLogResolveDirectory(softwareName string) (string, bool) { + var obrimLogDirectory string + + switch runtime.GOOS { + case "linux": + obrimLogHome, obrimLogHomeError := os.UserHomeDir() + if obrimLogHomeError != nil { + return "", false + } + + obrimLogDirectory = filepath.Join( + obrimLogHome, + ".local", + "state", + softwareName, + "log", + "main", + ) + + case "windows": + obrimLogLocalAppData := strings.TrimSpace(os.Getenv("LOCALAPPDATA")) + if obrimLogLocalAppData == "" { + return "", false + } + + obrimLogDirectory = filepath.Join( + obrimLogLocalAppData, + softwareName, + "log", + "main", + ) + + case "darwin": + obrimLogHome, obrimLogHomeError := os.UserHomeDir() + if obrimLogHomeError != nil { + return "", false + } + + obrimLogDirectory = filepath.Join( + obrimLogHome, + "Library", + "Logs", + softwareName, + "log", + "main", + ) + + default: + return "", false + } + + if obrimLogDirectory == "" { + return "", false + } + + if obrimLogMkdirError := os.MkdirAll(obrimLogDirectory, 0o755); obrimLogMkdirError != nil { + return "", false + } + + return obrimLogDirectory, true +} + +// obrimLogResolveFile resolves and creates the main log file. +func obrimLogResolveFile(directory string) (string, bool) { + obrimLogFile := filepath.Join(directory, "main.log") + + obrimLogHandle, obrimLogOpenError := os.OpenFile( + obrimLogFile, + os.O_CREATE|os.O_APPEND|os.O_WRONLY, + 0o644, + ) + if obrimLogOpenError != nil { + return "", false + } + + if obrimLogCloseError := obrimLogHandle.Close(); obrimLogCloseError != nil { + return "", false + } + + return obrimLogFile, true +} + +// obrimLogGetTimestamp gets the timestamp for a log entry. +func obrimLogGetTimestamp() string { + return time.Now().Format(time.RFC3339Nano) +} + +// obrimLogBuildEntry builds a complete formatted log entry. +func obrimLogBuildEntry(timestamp, label, message string) string { + return "[" + timestamp + "] [" + label + "] " + message + "\n" +} + +// obrimLogWriteEntry appends a complete log entry to the log file. +func obrimLogWriteEntry(file, entry string) { + obrimLogHandle, obrimLogOpenError := os.OpenFile( + file, + os.O_APPEND|os.O_WRONLY, + 0o644, + ) + if obrimLogOpenError != nil { + return + } + defer obrimLogHandle.Close() + + _, _ = obrimLogHandle.WriteString(entry) +}