Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Style Atmos Logger with Theme #1135

Open
wants to merge 41 commits into
base: main
Choose a base branch
from
Open

Conversation

Cerebrovinny
Copy link
Collaborator

@Cerebrovinny Cerebrovinny commented Mar 11, 2025

what

New Style for atmos logger with theme
Screenshot 2025-03-11 at 08 14 15
Screenshot 2025-03-11 at 08 09 49

why

references

Summary by CodeRabbit

  • New Features

    • Improved logging capabilities with additional granular log levels and enhanced output configuration.
    • Added a new log level, "AtmosTraceLevel", for more detailed logging.
  • Bug Fixes

    • Updated error messages for invalid log levels to reflect new valid options and their order.
  • Refactor

    • Streamlined log setup and error handling for a more robust and consistent logging experience.
    • Updated logging mechanism across various components to utilize the new logger structure, including changes in logger types and initialization methods.
    • Enhanced the logging output to provide more detailed information.
    • Adjusted the presentation of valid log levels in error messages for clarity.

@Cerebrovinny Cerebrovinny requested a review from osterman March 11, 2025 13:02
@Cerebrovinny Cerebrovinny marked this pull request as ready for review March 11, 2025 13:02
@Cerebrovinny Cerebrovinny requested a review from a team as a code owner March 11, 2025 13:02
Copy link
Contributor

coderabbitai bot commented Mar 11, 2025

📝 Walkthrough

Walkthrough

This pull request refactors the logging system throughout the codebase. The changes introduce a new logger type, AtmosLogger, and update log level handling by adding an "Error" case and a new log level (AtmosTraceLevel). Logger initialization is now performed with a pointer to the configuration, and tests have been updated accordingly. Changes affect command setup, CLI argument parsing, and the API client, ensuring that logger instances and methods are now consistent across modules.

Changes

File(s) Change Summary
cmd/root.go Updated setupLogger to include an "Error" log level and to create a new logger instance using NewAtmosLogger, then setting it as the global default.
pkg/logger/logger.go
pkg/logger/logger_test.go
Introduced AtmosLogger with new logging capabilities including the AtmosTraceLevel and LogLevelStrings; added utility functions (validateLogDestination, getLogWriter), enhanced logging methods, and updated tests to use log constants instead of strings.
internal/exec/describe_affected.go
internal/exec/pro.go
Modified CLI argument parsing to update logger fields to AtmosLogger and pass a pointer to the configuration; adjusted the log level setting from LogLevelTrace to AtmosTraceLevel.
pkg/pro/api_client.go
pkg/pro/api_client_test.go
Updated the API client's logger field and constructor signatures to use AtmosLogger; replaced string-based log level parameters in tests with the log.InfoLevel constant.

Sequence Diagram(s)

Loading
sequenceDiagram
    participant CLI
    participant CmdRoot
    participant LoggerFactory
    participant GlobalLogger

    CLI ->> CmdRoot: Calls setupLogger()
    CmdRoot ->> LoggerFactory: Invokes NewAtmosLogger(writer)
    LoggerFactory -->> CmdRoot: Returns AtmosLogger instance
    CmdRoot ->> GlobalLogger: Sets default logger via SetDefault(AtmosLogger)

Assessment against linked issues

Objective Addressed Explanation
Style Atmos Logger with Theme (DEV-3010) Logger styling (e.g., setting custom styles) is not configured.

Possibly related PRs

Tip

⚡🧪 Multi-step agentic review comment chat (experimental)
  • We're introducing multi-step agentic chat in review comments. This experimental feature enhances review discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments.
    - To enable this feature, set early_access to true under in the settings.

📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4820f56 and d83e510.

📒 Files selected for processing (1)
  • cmd/root.go (3 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
cmd/root.go (2)
pkg/logger/logger.go (3) (3)
  • NewAtmosLogger (213-247)
  • AtmosTraceLevel (21-21)
  • ParseLogLevel (129-148)
pkg/schema/schema.go (1) (1)
  • Logs (290-293)
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Acceptance Tests (macos-latest, macos)
  • GitHub Check: Acceptance Tests (windows-latest, windows)
  • GitHub Check: Acceptance Tests (ubuntu-latest, linux)
  • GitHub Check: [localstack] demo-localstack
  • GitHub Check: Summary
🔇 Additional comments (3)
cmd/root.go (3)

102-105: Logger initialization looks clean!

Nice implementation of the AtmosLogger setup. Using the NewAtmosLogger function to create a styled logger and setting it as the default is a good approach for consistent logging throughout the application.


108-123: The switch statement now properly handles all log levels.

The addition of the "Error" case addresses the previous feedback from @osterman. The implementation correctly sets log levels for all cases, including the special AtmosTraceLevel and Off (using math.MaxInt32).


129-139: Improved conditional logging logic.

The approach to use different log levels based on the configuration is solid. Using the lowercase version of the log level for consistency is a good practice.

For line 136, you're correctly using the log.Log method with the custom AtmosTraceLevel rather than an undefined log.Trace method, which fixes the previous build failure.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary or @auto-summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai or @auto-title anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Sorry, something went wrong.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 2

🧹 Nitpick comments (4)
cmd/root.go (1)

114-115: Use of SetDefault for a global logger.
Be mindful of potential concurrency scenarios; otherwise, this is fine.

pkg/logger/logger_test.go (2)

143-155: Error test uses fmt.Errorf, flagged by static analysis.
Consider using a package-level error if you want to avoid dynamic errors:

- err := fmt.Errorf("This is an error")
+ var errTest = errors.New("This is an error")
🧰 Tools
🪛 GitHub Check: golangci

[failure] 151-151: [golangci] pkg/logger/logger_test.go#L151
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("This is an error")" (err113)


156-168: File logger error test likewise uses a dynamic error.
Follow the same approach for consistency if avoiding dynamic errors:

- err2 := fmt.Errorf("This is a file error")
+ var errFileTest = errors.New("This is a file error")
🧰 Tools
🪛 GitHub Check: golangci

[failure] 164-164: [golangci] pkg/logger/logger_test.go#L164
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("This is a file error")" (err113)

pkg/logger/logger.go (1)

45-73: File writer logic and styled logger creation.
Addresses writing to various outputs but does not track file closure. Also note the static analysis suggestion about magic number 0o644:

- writer, err = os.OpenFile(file, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o644)
+ const filePermissions = 0o644
+ writer, err = os.OpenFile(file, os.O_WRONLY|os.O_APPEND|os.O_CREATE, filePermissions)
🧰 Tools
🪛 golangci-lint (1.62.2)

[error] 52-52: add-constant: avoid magic numbers like '0o644', create a named constant for it

(revive)

🪛 GitHub Check: golangci

[failure] 52-52: [golangci] pkg/logger/logger.go#L52
add-constant: avoid magic numbers like '0o644', create a named constant for it (revive)

🛑 Comments failed to post (2)
cmd/root.go (1)

117-131: ⚠️ Potential issue

Inconsistent handling of the 'Off' level compared to NewLogger.
Here, “Off” sets the log level to math.MaxInt32, but the logger package uses log.ErrorLevel for “Off.” This discrepancy could cause unpredictable logging behavior.

-	case "Off":
-		styledLog.SetLevel(math.MaxInt32)
+	case "Off":
+		styledLog.SetLevel(log.ErrorLevel)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

	// Set the appropriate log level based on configuration.
	switch atmosConfig.Logs.Level {
	case "Trace":
		styledLog.SetLevel(logger.AtmosTraceLevel)
	case "Debug":
		styledLog.SetLevel(log.DebugLevel)
	case "Info":
		styledLog.SetLevel(log.InfoLevel)
	case "Warning":
		styledLog.SetLevel(log.WarnLevel)
	case "Off":
		styledLog.SetLevel(log.ErrorLevel)
	default:
		styledLog.SetLevel(log.InfoLevel)
	}
pkg/logger/logger.go (1)

145-161: 🛠️ Refactor suggestion

SetLogLevel logic.
There is a mismatch between “Off” here (mapped to ErrorLevel) and cmd/root.go (set to math.MaxInt32). A single approach ensures consistent suppression of output.

Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <erik@cloudposse.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
pkg/logger/logger.go (1)

118-135: 🛠️ Refactor suggestion

Duplicate file handling logic should be centralized.

This code replicates the file handling logic that now exists in the NewLogger function. Since we're now using the styled logger, we should update this method to use it or remove it if it's no longer needed.

Consider refactoring this to use the StyledLogger for all logging operations.

🧰 Tools
🪛 golangci-lint (1.62.2)

[error] 118-118: add-constant: avoid magic numbers like '0o644', create a named constant for it

(revive)


[error] 131-131: add-constant: string literal "%s\n" appears, at least, 4 times, create a named constant for it

(revive)

♻️ Duplicate comments (1)
pkg/logger/logger.go (1)

45-56: ⚠️ Potential issue

Reconsider default log output destination.

The current implementation sets os.Stdout as the default output writer. This could potentially break downstream commands that rely on atmos output, as JSON output and logs cannot coexist on stdout without causing parsing issues.

Consider making /dev/stderr the default to maintain backward compatibility and prevent unintended side effects.

-var writer io.Writer = os.Stdout
+var writer io.Writer = os.Stderr
🧰 Tools
🪛 golangci-lint (1.62.2)

[error] 52-52: add-constant: avoid magic numbers like '0o644', create a named constant for it

(revive)

🪛 GitHub Check: golangci

[failure] 52-52: [golangci] pkg/logger/logger.go#L52
add-constant: avoid magic numbers like '0o644', create a named constant for it (revive)

🧹 Nitpick comments (5)
pkg/logger/logger.go (5)

26-27: Consider using a relative definition for AtmosTraceLevel.

The AtmosTraceLevel is currently defined as a hard-coded -5 with a comment indicating that DebugLevel is -4. A more maintainable approach would be to define it relative to log.DebugLevel:

-const AtmosTraceLevel log.Level = -5 // DebugLevel is -4
+const AtmosTraceLevel log.Level = log.DebugLevel - 1

This makes the relationship clearer and avoids issues if the underlying library changes level values.


52-52: Create a named constant for file permission value.

Avoid using magic numbers like '0o644' directly in the code. Create a named constant to improve readability and maintainability.

+const defaultLogFilePermissions = 0o644
 
-writer, err = os.OpenFile(file, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o644)
+writer, err = os.OpenFile(file, os.O_WRONLY|os.O_APPEND|os.O_CREATE, defaultLogFilePermissions)
🧰 Tools
🪛 golangci-lint (1.62.2)

[error] 52-52: add-constant: avoid magic numbers like '0o644', create a named constant for it

(revive)

🪛 GitHub Check: golangci

[failure] 52-52: [golangci] pkg/logger/logger.go#L52
add-constant: avoid magic numbers like '0o644', create a named constant for it (revive)


59-59: Rename logger creation function to match intention.

The function name should reflect that this is specifically an Atmos-themed logger, as noted in previous feedback.

-styledLogger := NewStyledLogger(writer)
+styledLogger := NewAtmosLogger(writer)

166-167: Revisit conditional Error logging.

The condition l.LogLevel != LogLevelOff might not be necessary since the StyledLogger already regulates output based on its configured level (set in SetLogLevel). Consider simplifying this:

-if err != nil && l.LogLevel != LogLevelOff {
+if err != nil {
    l.StyledLogger.Error("Error occurred", "error", err)
}

223-224: Update function name and documentation to better reflect purpose.

Rename the function and update its documentation to clearly indicate this is an Atmos-specific logger implementation:

-// NewStyledLogger creates a new styled Charmbracelet logger using Atmos theme colors.
-func NewStyledLogger(writer io.Writer) *log.Logger {
+// NewAtmosLogger creates a new Charmbracelet logger styled with Atmos theme colors.
+func NewAtmosLogger(writer io.Writer) *log.Logger {
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e54747d and 9309d1f.

📒 Files selected for processing (2)
  • pkg/logger/logger.go (5 hunks)
  • pkg/logger/logger.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/logger/logger.go
🧰 Additional context used
🪛 golangci-lint (1.62.2)
pkg/logger/logger.go

[error] 52-52: add-constant: avoid magic numbers like '0o644', create a named constant for it

(revive)

🪛 GitHub Check: golangci
pkg/logger/logger.go

[failure] 52-52: [golangci] pkg/logger/logger.go#L52
add-constant: avoid magic numbers like '0o644', create a named constant for it (revive)

⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: [mock-windows] tests/fixtures/scenarios/complete
  • GitHub Check: [mock-windows] examples/demo-vendoring
  • GitHub Check: Acceptance Tests (macos-latest, macos)
  • GitHub Check: Acceptance Tests (windows-latest, windows)
  • GitHub Check: Acceptance Tests (ubuntu-latest, linux)
  • GitHub Check: [localstack] demo-localstack
  • GitHub Check: Summary
🔇 Additional comments (2)
pkg/logger/logger.go (2)

234-234: LGTM - Great implementation of the trace level styling.

The implementation of the trace level styling is clean and consistent with the other log levels. The use of the Atmos theme colors provides a cohesive visual experience.


240-248: Strong approach to style organization.

Using a map to concisely declare key styles is a great pattern that improves maintainability. This makes it easy to add or modify key styles in the future.

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 11, 2025
Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <erik@cloudposse.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
pkg/logger/logger.go (3)

26-27: Consider defining AtmosTraceLevel relative to log.DebugLevel

The constant is correctly defined, but it would be more maintainable to define it relative to the DebugLevel in case that value changes in future library updates.

-const AtmosTraceLevel log.Level = -5 // DebugLevel is -4
+const AtmosTraceLevel log.Level = log.DebugLevel - 1

166-168: Consider simplifying error logging condition

The condition l.LogLevel != LogLevelOff may be redundant since you have the isLevelEnabled function that handles log level checking consistently.

-func (l *Logger) Error(err error) {
-	if err != nil && l.LogLevel != LogLevelOff {
-		l.StyledLogger.Error("Error occurred", "error", err)
-	}
-}
+func (l *Logger) Error(err error) {
+	if err != nil && l.isLevelEnabled(LogLevelWarning) {
+		l.StyledLogger.Error("Error occurred", "error", err)
+	}
+}

This would make the error handling consistent with other logging methods.


223-255: Well-implemented styled logger

The styled logger implementation is well-structured and makes good use of the theme colors from the theme package. The styling is consistent and provides good visual differentiation between log levels.

Consider renaming the function to NewAtmosLogger based on previous review comments to maintain consistency with the theme of the application.

-// NewStyledLogger creates a new styled Charmbracelet logger using Atmos theme colors.
-func NewStyledLogger(writer io.Writer) *log.Logger {
+// NewAtmosLogger creates a new Charmbracelet logger styled with Atmos theme colors.
+func NewAtmosLogger(writer io.Writer) *log.Logger {

And update all references to this function accordingly.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9309d1f and a25ed56.

📒 Files selected for processing (3)
  • pkg/logger/logger.go (5 hunks)
  • pkg/logger/logger.go (1 hunks)
  • pkg/logger/logger.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/logger/logger.go
  • pkg/logger/logger.go
🧰 Additional context used
🪛 GitHub Check: golangci
pkg/logger/logger.go

[failure] 59-59: [golangci] pkg/logger/logger.go#L59
undefined: NewStyledLogger) (typecheck)


[failure] 59-59: [golangci] pkg/logger/logger.go#L59
undefined: NewStyledLogger) (typecheck)


[failure] 59-59: [golangci] pkg/logger/logger.go#L59
undefined: NewStyledLogger) (typecheck)


[failure] 59-59: [golangci] pkg/logger/logger.go#L59
undefined: NewStyledLogger


[failure] 59-59: [golangci] pkg/logger/logger.go#L59
undefined: NewStyledLogger) (typecheck)

🪛 GitHub Check: Build (macos-latest, macos)
pkg/logger/logger.go

[failure] 59-59:
undefined: NewStyledLogger

🪛 GitHub Check: Build (ubuntu-latest, linux)
pkg/logger/logger.go

[failure] 59-59:
undefined: NewStyledLogger

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Summary
🔇 Additional comments (3)
pkg/logger/logger.go (3)

45-56: Writer selection logic looks good

The code correctly handles different output destinations, ensuring logs can be written to stdout, stderr, or a file as needed.


147-160: Log level mapping looks good

The implementation correctly maps the custom log levels to the Charmbracelet log levels, including the new trace level.


235-238: Good attention to error styling

The special styling for error values enhances readability and helps important errors stand out visually.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
pkg/logger/logger_test.go (1)

151-151: Address static analysis warning about dynamic errors.

The static analysis tool suggests using wrapped static errors instead of dynamic errors.

Consider defining static error variables at package level instead of using dynamic errors:

+var (
+    testError = errors.New("This is an error")
+    fileError = errors.New("This is a file error")
+)

- err := fmt.Errorf("This is an error")
+ err := testError

- err2 := fmt.Errorf("This is a file error")
+ err2 := fileError

Don't forget to add errors to your imports:

import (
    "bytes"
+   "errors"
    "fmt"
    // other imports...
)

Also applies to: 164-164

🧰 Tools
🪛 GitHub Check: golangci

[failure] 151-151: [golangci] pkg/logger/logger_test.go#L151
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("This is an error")" (err113)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a25ed56 and 1c8a36f.

📒 Files selected for processing (8)
  • cmd/root.go (3 hunks)
  • pkg/logger/logger.go (5 hunks)
  • pkg/logger/logger_test.go (2 hunks)
  • pkg/logger/logger.go (1 hunks)
  • pkg/logger/logger.go (1 hunks)
  • cmd/root.go (1 hunks)
  • pkg/logger/logger.go (6 hunks)
  • pkg/logger/logger_test.go (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • pkg/logger/logger.go
  • pkg/logger/logger.go
  • cmd/root.go
  • cmd/root.go
  • pkg/logger/logger_test.go
🧰 Additional context used
🪛 golangci-lint (1.62.2)
pkg/logger/logger.go

[error] 52-52: add-constant: avoid magic numbers like '0o644', create a named constant for it

(revive)

🪛 GitHub Check: golangci
pkg/logger/logger.go

[failure] 52-52: [golangci] pkg/logger/logger.go#L52
add-constant: avoid magic numbers like '0o644', create a named constant for it (revive)

pkg/logger/logger_test.go

[failure] 151-151: [golangci] pkg/logger/logger_test.go#L151
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("This is an error")" (err113)


[failure] 164-164: [golangci] pkg/logger/logger_test.go#L164
do not define dynamic errors, use wrapped static errors instead: "fmt.Errorf("This is a file error")" (err113)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Summary
🔇 Additional comments (12)
pkg/logger/logger.go (8)

39-41: Good renaming from StyledLogger to AtmosLogger.

This renaming aligns with the PR objectives to style the Atmos Logger with theme, making the code more descriptive of its purpose within the project.


58-59: Renaming correctly applied in logger creation.

The comment and function call have been consistently updated to use the new AtmosLogger name.


64-72: Log level settings properly updated.

All references to the logger field have been consistently renamed from StyledLogger to AtmosLogger.


76-78: Struct initialization updated correctly.

The Logger struct initialization has been updated to use the new AtmosLogger field name.


147-159: Log level setting logic updated correctly.

All references to the AtmosLogger in the SetLogLevel method have been properly renamed.


166-167: Error logging method updated correctly.

This addresses the previous review comment by osterman asking "What about Error?", ensuring consistent renaming across all logging methods.


179-181: All logging methods consistently updated.

The Trace, Debug, Info, and Warning methods have all been updated to use AtmosLogger.

Also applies to: 185-187, 191-193, 197-199


223-224: Function renamed and documentation updated.

The function has been renamed from NewStyledLogger to NewAtmosLogger, and the documentation has been updated to reflect this change.

pkg/logger/logger_test.go (4)

11-11: Import updated to use charmbracelet/log.

The import statement has been properly updated to use the charmbracelet/log package.


108-108: ⚠️ Potential issue

Fix inconsistent field reference.

This line still references StyledLogger but needs to be updated to AtmosLogger.

Apply this fix:

-  assert.NotNil(t, logger.StyledLogger)
+  assert.NotNil(t, logger.AtmosLogger)

Likely an incorrect or invalid review comment.


158-162: ⚠️ Potential issue

Update logger field and constructor in file logger test.

Both the field name and constructor call need to be updated to match the renamed AtmosLogger.

Apply this fix:

  fileLogger := &Logger{
    LogLevel:     LogLevelWarning,
    File:         "test.log",
-   StyledLogger: NewStyledLogger(&buf2),
+   AtmosLogger:  NewAtmosLogger(&buf2),
  }

Likely an incorrect or invalid review comment.


145-149: ⚠️ Potential issue

Update logger field and constructor in Error test.

Both the field name and constructor call need to be updated to match the renamed AtmosLogger.

Apply this fix:

  logger := &Logger{
    LogLevel:     LogLevelWarning,
    File:         "/dev/stderr",
-   StyledLogger: NewStyledLogger(&buf),
+   AtmosLogger:  NewAtmosLogger(&buf),
  }

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
pkg/logger/logger.go (1)

46-46: ⚠️ Potential issue

Default logging to stderr instead of stdout.

Setting stdout as the default writer may interfere with command output parsing. Previous discussions indicated stderr should be the default to avoid breaking commands that depend on parsing Atmos output.

- var writer io.Writer = os.Stdout
+ var writer io.Writer = os.Stderr

This aligns with the previous recommendation that "/dev/stderr" should be the default log destination.

🧹 Nitpick comments (5)
pkg/logger/logger.go (5)

26-27: Consider deriving AtmosTraceLevel from log.DebugLevel for clearer relationship.

The definition of AtmosTraceLevel as a fixed value -5 is functional but could be more maintainable by deriving it directly from the DebugLevel constant.

- const AtmosTraceLevel log.Level = -5 // DebugLevel is -4
+ const AtmosTraceLevel log.Level = log.DebugLevel - 1

This approach makes the relationship between log levels explicit and will automatically adapt if the base levels change in the future.


44-56: Extract file permission constant for better maintainability.

The magic number 0o644 for file permissions should be extracted into a named constant.

+// DefaultFilePermissions represents standard file permissions for log files (rw-r--r--)
+const DefaultFilePermissions = 0o644

func NewLogger(logLevel LogLevel, file string) (*Logger, error) {
	// Determine the output writer based on file path.
	var writer io.Writer = os.Stdout
	if file == "/dev/stderr" {
		writer = os.Stderr
	} else if file != "" && file != "/dev/stdout" {
		// For actual files, we'll use a file writer.
		var err error
-		writer, err = os.OpenFile(file, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o644)
+		writer, err = os.OpenFile(file, os.O_WRONLY|os.O_APPEND|os.O_CREATE, DefaultFilePermissions)

This will also need to be updated at line 118 where the same magic number appears.

🧰 Tools
🪛 golangci-lint (1.62.2)

[error] 52-52: add-constant: avoid magic numbers like '0o644', create a named constant for it

(revive)

🪛 GitHub Check: golangci

[failure] 52-52: [golangci] pkg/logger/logger.go#L52
add-constant: avoid magic numbers like '0o644', create a named constant for it (revive)


166-168: Simplify error logging by removing redundant condition.

The condition check is unnecessary since the StyledLogger already handles log level filtering based on its configured level.

- func (l *Logger) Error(err error) {
-   if err != nil && l.LogLevel != LogLevelOff {
-     l.StyledLogger.Error("Error occurred", "error", err)
-   }
- }
+ func (l *Logger) Error(err error) {
+   if err != nil {
+     l.StyledLogger.Error("Error occurred", "error", err)
+   }
+ }

This simplifies the code and relies on the StyledLogger's level configuration to control output.


179-182: Simplify logging methods by removing redundant level checks.

All the logging methods (Trace, Debug, Info, Warning) include level checks that are redundant when using StyledLogger, which already handles level filtering.

For the Trace method:

func (l *Logger) Trace(message string) {
-  if l.isLevelEnabled(LogLevelTrace) {
    l.StyledLogger.Log(AtmosTraceLevel, message)
-  }
}

Similar changes should be applied to the Debug, Info, and Warning methods as well.


224-255: Function needs to be moved earlier in the file.

The NewStyledLogger function is called at line 59 but defined much later. While Go allows this, it's better practice to define functions before they're used.

Move this function definition to before line 44 where NewLogger starts, to improve code readability and organization.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c8a36f and 3a026e3.

📒 Files selected for processing (5)
  • pkg/logger/logger.go (5 hunks)
  • pkg/logger/logger.go (1 hunks)
  • pkg/logger/logger.go (1 hunks)
  • pkg/logger/logger.go (6 hunks)
  • pkg/logger/logger.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • pkg/logger/logger.go
🚧 Files skipped from review as they are similar to previous changes (3)
  • pkg/logger/logger.go
  • pkg/logger/logger.go
  • pkg/logger/logger.go
🧰 Additional context used
🪛 golangci-lint (1.62.2)
pkg/logger/logger.go

[error] 52-52: add-constant: avoid magic numbers like '0o644', create a named constant for it

(revive)

🪛 GitHub Check: golangci
pkg/logger/logger.go

[failure] 52-52: [golangci] pkg/logger/logger.go#L52
add-constant: avoid magic numbers like '0o644', create a named constant for it (revive)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: [mock-windows] tests/fixtures/scenarios/complete
  • GitHub Check: Acceptance Tests (macos-latest, macos)
  • GitHub Check: Acceptance Tests (windows-latest, windows)
  • GitHub Check: Acceptance Tests (ubuntu-latest, linux)
  • GitHub Check: [localstack] demo-localstack
  • GitHub Check: Summary
🔇 Additional comments (1)
pkg/logger/logger.go (1)

223-224: Rename function to 'NewAtmosLogger' for consistency.

Based on previous feedback, this function should be renamed to explicitly reference Atmos in its name.

- // NewStyledLogger creates a new styled Charmbracelet logger using Atmos theme colors.
- func NewStyledLogger(writer io.Writer) *log.Logger {
+ // NewAtmosLogger creates a new Charmbracelet logger styled with Atmos theme colors.
+ func NewAtmosLogger(writer io.Writer) *log.Logger {

Also update all references to this function in the codebase, such as at line 59.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
pkg/logger/logger.go (1)

31-36: 🛠️ Refactor suggestion

Remove the duplicate LogLevelDebug entry in logLevelOrder.

Currently, logLevelOrder contains two identical entries of LogLevelDebug, which is likely an oversight that can create confusion. Consider removing one occurrence to ensure clarity.

 var logLevelOrder = map[LogLevel]int{
   LogLevelTrace:   0,
   LogLevelDebug:   1,
-  LogLevelDebug:   1,
   LogLevelInfo:    2,
   LogLevelWarning: 3,
 }
🧹 Nitpick comments (1)
pkg/logger/logger.go (1)

52-52: Avoid using the raw numeric file mode 0o644.

Defining it as a named constant or a variable makes the code more expressive and helps avoid repetition.

- writer, err = os.OpenFile(file, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o644)
+ const filePerm os.FileMode = 0o644
+ writer, err = os.OpenFile(file, os.O_WRONLY|os.O_APPEND|os.O_CREATE, filePerm)

Also applies to: 122-122

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a026e3 and 0dd18a2.

📒 Files selected for processing (6)
  • pkg/logger/logger.go (5 hunks)
  • pkg/logger/logger.go (1 hunks)
  • pkg/logger/logger.go (1 hunks)
  • pkg/logger/logger.go (6 hunks)
  • pkg/logger/logger.go (1 hunks)
  • pkg/logger/logger.go (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • pkg/logger/logger.go
  • pkg/logger/logger.go
  • pkg/logger/logger.go
🧰 Additional context used
🪛 golangci-lint (1.62.2)
pkg/logger/logger.go

[error] 46-46: cyclomatic: function NewLogger has cyclomatic complexity 11 (> max enabled 10)

(revive)


[error] 54-54: add-constant: avoid magic numbers like '0o644', create a named constant for it

(revive)

🪛 GitHub Check: golangci
pkg/logger/logger.go

[failure] 54-54: [golangci] pkg/logger/logger.go#L54
add-constant: avoid magic numbers like '0o644', create a named constant for it (revive)


[failure] 46-46: [golangci] pkg/logger/logger.go#L46
cyclomatic: function NewLogger has cyclomatic complexity 11 (> max enabled 10) (revive)

⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Acceptance Tests (macos-latest, macos)
  • GitHub Check: Acceptance Tests (windows-latest, windows)
  • GitHub Check: Acceptance Tests (ubuntu-latest, linux)
  • GitHub Check: [localstack] demo-localstack
  • GitHub Check: Summary
🔇 Additional comments (1)
pkg/logger/logger.go (1)

99-106: Ensure LogLevelError is declared before usage.

LogLevelError is included in validLevels yet is not defined in your constants. Either define it in the log-level enum or remove it from validLevels to maintain consistency.

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 18, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
pkg/logger/logger.go (2)

127-140: Include "Error" in validLevels list.

The validLevels slice should include "Error" since it's supported in the LogLevelStrings map.

-	validLevels := []string{"Trace", "Debug", "Info", "Warning", "Off"}
+	validLevels := []string{"Trace", "Debug", "Info", "Warning", "Error", "Off"}

139-139: Use static error pattern consistently.

This line creates a dynamic error message with fmt.Errorf, which was flagged in previous static analysis. Consider using a custom error type pattern like you did with ErrUnsupportedDeviceFile.

-	return 0, fmt.Errorf("%w `%s`. Valid options are: %v", ErrInvalidLogLevel, logLevel, validLevels)
+	return 0, &InvalidLogLevelError{LogLevel: logLevel, ValidLevels: validLevels, Err: ErrInvalidLogLevel}

And add this error type:

// InvalidLogLevelError represents an error when an invalid log level is provided
type InvalidLogLevelError struct {
	LogLevel    string
	ValidLevels []string
	Err         error
}

func (e *InvalidLogLevelError) Error() string {
	return fmt.Sprintf("%v `%s`. Valid options are: %v", e.Err, e.LogLevel, e.ValidLevels)
}

func (e *InvalidLogLevelError) Unwrap() error {
	return e.Err
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 58fc21e and 7b25bed.

📒 Files selected for processing (1)
  • pkg/logger/logger.go (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Build (macos-latest, macos)
  • GitHub Check: Analyze (go)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Lint (golangci)
  • GitHub Check: Summary
🔇 Additional comments (11)
pkg/logger/logger.go (11)

17-17: Good addition of FilePermission constant.

This addresses the "magic number" issue flagged in static analysis. Using a named constant improves code readability and maintainability.


19-20: Well-defined custom log level.

Good implementation of AtmosTraceLevel as one level below DebugLevel for more granular logging control.


22-30: Clean log level mapping implementation.

The LogLevelStrings map provides a clean way to map string representations to charmbracelet log levels, with proper handling of the "Off" level.


32-43: Good implementation of static errors.

This addresses the previous code scanning alerts by implementing static errors instead of dynamic errors, which is a Go best practice.


45-47: Clean AtmosLogger implementation.

The AtmosLogger struct properly embeds the charmbracelet logger, following the composition pattern recommended in Go.


49-68: Robust log destination validation.

The validation logic properly handles standard device files and provides specific warnings for stdout.


70-94: Comprehensive log writer resolution.

The getLogWriter function handles all common log destinations with appropriate warnings and fallbacks.


96-117: Streamlined logger creation.

The refactored NewLogger function has a cleaner flow with proper validation and error handling.


152-156: Consistent error handling.

The Error method properly checks the log level before logging.


193-202: Well-structured level styling function.

The createLevelStyle function provides a clean way to create consistent level styles.


204-239: Comprehensive Atmos theme implementation.

The NewAtmosLogger function provides a well-styled logger with custom colors for different log levels and key types.

Consider using different colors for Error and Warning levels to improve visual distinction:

-	styles.Levels[log.WarnLevel] = createLevelStyle(warnLevelLabel, theme.ColorRed)
+	styles.Levels[log.WarnLevel] = createLevelStyle(warnLevelLabel, theme.ColorOrange)

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 18, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
cmd/root.go (1)

83-100: ⚠️ Potential issue

Avoid closing the log file prematurely.

Placing defer logFile.Close() inside setupLogger will close the file handle once the function returns, preventing further writes. Consider keeping the file open for the application’s lifecycle:

 func setupLogger(atmosConfig *schema.AtmosConfiguration) {
     ...
     logFile, err := os.OpenFile(atmosConfig.Logs.File, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666)
     if err != nil {
         log.Fatal("Failed to open log file:", err)
     }
-    defer logFile.Close()
     ...
 }
🧹 Nitpick comments (2)
cmd/root.go (2)

129-133: Minor note on default behavior.

If logLevelStr is empty, it’s set to "info". This is fine, but confirm it aligns with user expectations when no level is specified.


135-139: Consider unifying the trace vs debug logging approach.

You log setup details at AtmosTraceLevel for trace, but log.Debug for everything else. It might be simpler to adopt a consistent approach if you foresee more trace-level setups.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b25bed and ece8630.

📒 Files selected for processing (1)
  • cmd/root.go (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: Acceptance Tests (macos-latest, macos)
  • GitHub Check: [localstack] demo-localstack
  • GitHub Check: Acceptance Tests (windows-latest, windows)
  • GitHub Check: Acceptance Tests (ubuntu-latest, linux)
  • GitHub Check: Summary
🔇 Additional comments (2)
cmd/root.go (2)

12-12: Looks good adopting the Charmbracelet log import.

No concerns here. The new import aligns well with your updated logging approach.


102-123: New logger initialization looks excellent.

It's good to see the newly included "Error" level handled. The approach for "Off" using math.MaxInt32 effectively disables logging. Nicely done.

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 18, 2025
coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 19, 2025
@osterman
Copy link
Member

Please post an updated screenshot as evidence.

}

if strings.ToLower(atmosConfig.Logs.Level) == "trace" {
log.Log(logger.AtmosTraceLevel, "Set", "logs-level", logLevelStr, "logs-file", atmosConfig.Logs.File)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
log.Log(logger.AtmosTraceLevel, "Set", "logs-level", logLevelStr, "logs-file", atmosConfig.Logs.File)
log.Trace("Set", "logs-level", logLevelStr, "logs-file", atmosConfig.Logs.File)

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 19, 2025
Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <erik@cloudposse.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fde8f5e and 4820f56.

📒 Files selected for processing (1)
  • cmd/root.go (3 hunks)
🧰 Additional context used
🪛 GitHub Check: Build (macos-latest, macos)
cmd/root.go

[failure] 136-136:
undefined: log.Trace

🪛 GitHub Check: Build (ubuntu-latest, linux)
cmd/root.go

[failure] 136-136:
undefined: log.Trace

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Build (windows-latest, windows)
  • GitHub Check: Summary
🔇 Additional comments (5)
cmd/root.go (5)

12-12: Great choice on using charmbracelet/log for styled logging

The switch to using the charmbracelet/log package aligns well with the PR objective of styling the Atmos logger. This package provides excellent support for structured, styled logging in terminal applications.


102-105: Good implementation of the styled logger

Creating a new AtmosLogger and setting it as the default logger is the right approach. This cleanly establishes the styled logger as the application's primary logging mechanism.


108-123: Comprehensive log level handling

The switch statement now properly handles all log levels including the "Error" case that was previously requested. The addition of AtmosTraceLevel is also a good enhancement for more detailed logging when needed.


129-134: Good improvement to user feedback

Using the exact log level string from the config provides more consistent output and better user experience. The default fallback to "info" is also a sensible approach.


82-100: Keep the output determination logic unchanged

You've correctly maintained the existing logic for determining the output writer based on configuration, which ensures backward compatibility.

This reverts commit 4820f56.
@Cerebrovinny
Copy link
Collaborator Author

Screenshot 2025-03-19 at 19 40 25
Screenshot 2025-03-19 at 19 40 17

@Cerebrovinny Cerebrovinny requested a review from osterman March 19, 2025 19:41
@@ -1,3 +1,3 @@
DEBU Set logs-level=debug logs-file=/dev/stderr
TRCE Set logs-level=trace logs-file=/dev/stderr
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wierd to me. We shouldn't dynamically set the log level of the log output based on the configuration.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, never mind. I thought we were doing something dynamically, such that if I set Log level to Info, that this would then say:

INFO Set logs-level=info logs-file=/dev/stderr

Please confirm this is not the case.

if strings.ToLower(atmosConfig.Logs.Level) == "trace" {
log.Log(logger.AtmosTraceLevel, "Set", "logs-level", logLevelStr, "logs-file", atmosConfig.Logs.File)
} else {
log.Debug("Set", "logs-level", logLevelStr, "logs-file", atmosConfig.Logs.File)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we use log.Debug, but in the logs we output TRCE

We should be consistent.

https://github.com/cloudposse/atmos/pull/1135/files#r2004173296

I'm okay changing it to Trace, but then we should have it be that consistently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
minor New features that do not break anything size/l
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants