Skip to content

Standardize API error logs#70

Merged
julsemaan merged 3 commits into
masterfrom
chore/api-error-logs
Apr 15, 2026
Merged

Standardize API error logs#70
julsemaan merged 3 commits into
masterfrom
chore/api-error-logs

Conversation

@julsemaan
Copy link
Copy Markdown
Owner

fixes #69

@julsemaan julsemaan requested a review from Copilot April 13, 2026 23:55
@github-actions
Copy link
Copy Markdown

Go coverage report (utils)

github.com/julsemaan/anyfile-notepad/utils/utils.go:16:	SendEmail		100.0%
github.com/julsemaan/anyfile-notepad/utils/utils.go:48:	sendMailWithTLSConfig	0.0%
total:							(statements)		46.5%

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 13, 2026

Go coverage report (api)

github.com/julsemaan/anyfile-notepad/api/cmd/api/main.go:8:				main				0.0%
github.com/julsemaan/anyfile-notepad/api/internal/app/config.go:23:			LoadConfigFromEnv		100.0%
github.com/julsemaan/anyfile-notepad/api/internal/app/config.go:45:			loadMaxContactRequestsPerDay	100.0%
github.com/julsemaan/anyfile-notepad/api/internal/app/email.go:17:			sendEmailWithOptionalTLS	100.0%
github.com/julsemaan/anyfile-notepad/api/internal/app/email.go:48:			sendMailWithTLSConfig		0.0%
github.com/julsemaan/anyfile-notepad/api/internal/app/run.go:20:			Run				0.0%
github.com/julsemaan/anyfile-notepad/api/internal/contact/service.go:39:		NewService			100.0%
github.com/julsemaan/anyfile-notepad/api/internal/contact/service.go:48:		BeforeInsert			85.7%
github.com/julsemaan/anyfile-notepad/api/internal/contact/service.go:73:		AfterInsert			100.0%
github.com/julsemaan/anyfile-notepad/api/internal/contact/service.go:87:		buildMessage			70.0%
github.com/julsemaan/anyfile-notepad/api/internal/httpapi/handler_stats.go:17:		NewStatsHandler			100.0%
github.com/julsemaan/anyfile-notepad/api/internal/httpapi/middleware_access_log.go:14:	WriteHeader			100.0%
github.com/julsemaan/anyfile-notepad/api/internal/httpapi/middleware_access_log.go:19:	withAccessLog			100.0%
github.com/julsemaan/anyfile-notepad/api/internal/httpapi/middleware_auth.go:10:	IsOpenResource			100.0%
github.com/julsemaan/anyfile-notepad/api/internal/httpapi/middleware_auth.go:30:	Authenticate			100.0%
github.com/julsemaan/anyfile-notepad/api/internal/httpapi/middleware_cors.go:5:		withCORS			100.0%
github.com/julsemaan/anyfile-notepad/api/internal/httpapi/router.go:10:			NewRouter			100.0%
github.com/julsemaan/anyfile-notepad/api/internal/logging/logging.go:12:		output				100.0%
github.com/julsemaan/anyfile-notepad/api/internal/logging/logging.go:16:		Error				60.0%
github.com/julsemaan/anyfile-notepad/api/internal/logging/logging.go:26:		Errorf				100.0%
github.com/julsemaan/anyfile-notepad/api/internal/logging/logging.go:30:		Fatalf				100.0%
github.com/julsemaan/anyfile-notepad/api/internal/resources/index.go:13:		BuildIndex			0.0%
github.com/julsemaan/anyfile-notepad/api/internal/resources/schema.go:19:		MimeTypeSchema			0.0%
github.com/julsemaan/anyfile-notepad/api/internal/resources/schema.go:43:		ExtensionSchema			0.0%
github.com/julsemaan/anyfile-notepad/api/internal/resources/schema.go:72:		SyntaxSchema			0.0%
github.com/julsemaan/anyfile-notepad/api/internal/resources/schema.go:91:		SettingSchema			0.0%
github.com/julsemaan/anyfile-notepad/api/internal/resources/schema.go:110:		ContactRequestSchema		0.0%
github.com/julsemaan/anyfile-notepad/api/internal/resources/schema.go:136:		Validate			100.0%
github.com/julsemaan/anyfile-notepad/api/internal/stats/service.go:31:			NewService			100.0%
github.com/julsemaan/anyfile-notepad/api/internal/stats/service.go:35:			ParsePayload			89.5%
github.com/julsemaan/anyfile-notepad/api/internal/stats/service.go:67:			Record				85.7%
github.com/julsemaan/anyfile-notepad/api/internal/stats/service.go:82:			normalizeIPMetricKey		76.9%
github.com/julsemaan/anyfile-notepad/api/internal/stats/service.go:107:			extractIP			60.0%
total:											(statements)			68.9%

@github-actions
Copy link
Copy Markdown

Go coverage report (webserver)

github.com/julsemaan/anyfile-notepad/webserver/billing_handlers.go:38:				renderEmailTemplate		85.7%
github.com/julsemaan/anyfile-notepad/webserver/billing_handlers.go:52:				LoadSubscription		100.0%
github.com/julsemaan/anyfile-notepad/webserver/billing_handlers.go:71:				LoadGoogleUser			96.2%
github.com/julsemaan/anyfile-notepad/webserver/billing_handlers.go:122:				handleSubscriptionRead		100.0%
github.com/julsemaan/anyfile-notepad/webserver/billing_handlers.go:126:				handleSubscriptionResume	100.0%
github.com/julsemaan/anyfile-notepad/webserver/billing_handlers.go:153:				handleSubscriptionCancel	86.7%
github.com/julsemaan/anyfile-notepad/webserver/billing_handlers.go:182:				handleSubscriptionUpgrade	100.0%
github.com/julsemaan/anyfile-notepad/webserver/billing_handlers.go:231:				upgradeError			100.0%
github.com/julsemaan/anyfile-notepad/webserver/billing_handlers.go:238:				handleLinkCancel		60.0%
github.com/julsemaan/anyfile-notepad/webserver/billing_handlers.go:294:				handleStripeHook		72.4%
github.com/julsemaan/anyfile-notepad/webserver/cluster_observer.go:16:				NewClusterObserver		100.0%
github.com/julsemaan/anyfile-notepad/webserver/cluster_observer.go:20:				hostURL				75.0%
github.com/julsemaan/anyfile-notepad/webserver/cluster_observer.go:28:				glpclient			100.0%
github.com/julsemaan/anyfile-notepad/webserver/cluster_observer.go:40:				Start				0.0%
github.com/julsemaan/anyfile-notepad/webserver/email.go:16:					sendEmailWithOptionalTLS	100.0%
github.com/julsemaan/anyfile-notepad/webserver/email.go:47:					sendMailWithTLSConfig		0.0%
github.com/julsemaan/anyfile-notepad/webserver/emails_update/update_customers_emails.go:24:	Next				0.0%
github.com/julsemaan/anyfile-notepad/webserver/emails_update/update_customers_emails.go:28:	Customer			0.0%
github.com/julsemaan/anyfile-notepad/webserver/emails_update/update_customers_emails.go:32:	Err				0.0%
github.com/julsemaan/anyfile-notepad/webserver/emails_update/update_customers_emails.go:42:	main				0.0%
github.com/julsemaan/anyfile-notepad/webserver/emails_update/update_customers_emails.go:47:	updateCustomerEmails		100.0%
github.com/julsemaan/anyfile-notepad/webserver/http_handler.go:12:				ServeHTTP			100.0%
github.com/julsemaan/anyfile-notepad/webserver/http_handler.go:24:				setupPlusPlusSession		75.0%
github.com/julsemaan/anyfile-notepad/webserver/http_handler.go:40:				ServeStaticApplication		100.0%
github.com/julsemaan/anyfile-notepad/webserver/main.go:51:					main				0.0%
github.com/julsemaan/anyfile-notepad/webserver/main.go:71:					setupSessionsPersistence	0.0%
github.com/julsemaan/anyfile-notepad/webserver/main.go:95:					setupHandlers			93.3%
github.com/julsemaan/anyfile-notepad/webserver/main.go:147:					setupSubscriptions		0.0%
github.com/julsemaan/anyfile-notepad/webserver/main.go:165:					setupClusterObserver		0.0%
github.com/julsemaan/anyfile-notepad/webserver/main.go:178:					setupBlockedUsersMap		100.0%
github.com/julsemaan/anyfile-notepad/webserver/main.go:184:					setup				0.0%
github.com/julsemaan/anyfile-notepad/webserver/oauth2_handlers.go:23:				getGoogleOauth2Conf		100.0%
github.com/julsemaan/anyfile-notepad/webserver/oauth2_handlers.go:43:				handleGoogleOauth2Authorize	100.0%
github.com/julsemaan/anyfile-notepad/webserver/oauth2_handlers.go:55:				handleGoogleOauth2Callback	100.0%
github.com/julsemaan/anyfile-notepad/webserver/plus_plus_session.go:26:				NewPlusPlusSession		100.0%
github.com/julsemaan/anyfile-notepad/webserver/plus_plus_session.go:38:				NewPlusPlusSessions		100.0%
github.com/julsemaan/anyfile-notepad/webserver/plus_plus_session.go:45:				GenerateSessionID		80.0%
github.com/julsemaan/anyfile-notepad/webserver/plus_plus_session.go:62:				Set				100.0%
github.com/julsemaan/anyfile-notepad/webserver/plus_plus_session.go:68:				Get				100.0%
github.com/julsemaan/anyfile-notepad/webserver/plus_plus_session.go:74:				Maintenance			100.0%
github.com/julsemaan/anyfile-notepad/webserver/plus_plus_session.go:92:				RestoreFromFile			88.9%
github.com/julsemaan/anyfile-notepad/webserver/plus_plus_session.go:108:			SaveToFile			68.4%
github.com/julsemaan/anyfile-notepad/webserver/realtime_handler.go:13:				publishRealtimeEvent		100.0%
github.com/julsemaan/anyfile-notepad/webserver/subscriptions.go:23:				Next				0.0%
github.com/julsemaan/anyfile-notepad/webserver/subscriptions.go:27:				Sub				0.0%
github.com/julsemaan/anyfile-notepad/webserver/subscriptions.go:31:				Err				0.0%
github.com/julsemaan/anyfile-notepad/webserver/subscriptions.go:53:				NewSubscriptions		100.0%
github.com/julsemaan/anyfile-notepad/webserver/subscriptions.go:60:				Empty				100.0%
github.com/julsemaan/anyfile-notepad/webserver/subscriptions.go:64:				CanHaveAccess			100.0%
github.com/julsemaan/anyfile-notepad/webserver/subscriptions.go:68:				ExtractUserId			75.0%
github.com/julsemaan/anyfile-notepad/webserver/subscriptions.go:76:				SetSubscription			100.0%
github.com/julsemaan/anyfile-notepad/webserver/subscriptions.go:90:				DelSubscription			100.0%
github.com/julsemaan/anyfile-notepad/webserver/subscriptions.go:97:				GetSubscription			100.0%
github.com/julsemaan/anyfile-notepad/webserver/subscriptions.go:104:				GetSubscriptionByCustomer	100.0%
github.com/julsemaan/anyfile-notepad/webserver/subscriptions.go:117:				Reload				100.0%
github.com/julsemaan/anyfile-notepad/webserver/subscriptions.go:145:				Maintenance			83.3%
github.com/julsemaan/anyfile-notepad/webserver/utils.go:10:					secureRandomString		60.0%
github.com/julsemaan/anyfile-notepad/webserver/utils.go:19:					InfoPrint			100.0%
github.com/julsemaan/anyfile-notepad/webserver/utils.go:24:					ErrPrint			100.0%
github.com/julsemaan/anyfile-notepad/webserver/utils.go:29:					EnvOrDefault			100.0%
total:												(statements)			73.1%

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a small internal logging wrapper to standardize error log prefixes across the API, addressing issue #69 (“ERROR:” prefix on error log lines).

Changes:

  • Introduce api/internal/logging helpers (Error, Errorf, Fatalf) that prefix logs with ERROR:.
  • Update contact/email/app startup code paths to use the new helpers instead of log/fmt for error logging.
  • Ensure the API entrypoint uses the standardized fatal error logging.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
api/internal/logging/logging.go Adds centralized error logging helpers that prepend ERROR:.
api/internal/contact/service.go Routes email-building/sending error logs through internal/logging.
api/internal/app/run.go Standardizes statsd init failure logging via internal/logging.
api/internal/app/email.go Replaces fmt.Println error output with standardized logging helper.
api/cmd/api/main.go Replaces log.Fatal with standardized logging.Fatalf.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +5 to +16
func Error(args ...interface{}) {
args = append([]interface{}{"ERROR:"}, args...)
log.Println(args...)
}

func Errorf(format string, args ...interface{}) {
log.Printf("ERROR: "+format, args...)
}

func Fatalf(format string, args ...interface{}) {
log.Fatalf("ERROR: "+format, args...)
}
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

The new logging package doesn’t have any unit tests to assert the required "ERROR:" prefix behavior. Since other internal packages are covered by tests, consider adding a small test that captures log output and verifies that Error/Errorf/Fatalf all emit messages containing the standardized prefix.

Copilot uses AI. Check for mistakes.
Comment thread api/internal/logging/logging.go Outdated
Comment on lines +5 to +15
func Error(args ...interface{}) {
args = append([]interface{}{"ERROR:"}, args...)
log.Println(args...)
}

func Errorf(format string, args ...interface{}) {
log.Printf("ERROR: "+format, args...)
}

func Fatalf(format string, args ...interface{}) {
log.Fatalf("ERROR: "+format, args...)
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

The "ERROR:" prefix is duplicated and applied slightly differently across helpers (Error prepends "ERROR:" as an arg, while Errorf/Fatalf embed "ERROR: " in the format string). Consider centralizing the prefix in a constant and applying it consistently so log parsing is predictable (including spacing).

Suggested change
func Error(args ...interface{}) {
args = append([]interface{}{"ERROR:"}, args...)
log.Println(args...)
}
func Errorf(format string, args ...interface{}) {
log.Printf("ERROR: "+format, args...)
}
func Fatalf(format string, args ...interface{}) {
log.Fatalf("ERROR: "+format, args...)
const errorPrefix = "ERROR: "
func Error(args ...interface{}) {
args = append([]interface{}{errorPrefix}, args...)
log.Println(args...)
}
func Errorf(format string, args ...interface{}) {
log.Printf(errorPrefix+format, args...)
}
func Fatalf(format string, args ...interface{}) {
log.Fatalf(errorPrefix+format, args...)

Copilot uses AI. Check for mistakes.
Comment thread api/internal/logging/logging.go Outdated
Comment on lines +3 to +15
import "log"

func Error(args ...interface{}) {
args = append([]interface{}{"ERROR:"}, args...)
log.Println(args...)
}

func Errorf(format string, args ...interface{}) {
log.Printf("ERROR: "+format, args...)
}

func Fatalf(format string, args ...interface{}) {
log.Fatalf("ERROR: "+format, args...)
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

These helpers wrap the stdlib log package, which means if log flags ever include file/line (e.g., Lshortfile/Llongfile), the reported callsite will be inside logging.go rather than the original caller. To preserve correct callsites, consider using log.Default().Output with an adjusted call depth (and formatting the message yourself) instead of calling log.Printf/log.Println directly.

Suggested change
import "log"
func Error(args ...interface{}) {
args = append([]interface{}{"ERROR:"}, args...)
log.Println(args...)
}
func Errorf(format string, args ...interface{}) {
log.Printf("ERROR: "+format, args...)
}
func Fatalf(format string, args ...interface{}) {
log.Fatalf("ERROR: "+format, args...)
import (
"fmt"
"log"
"os"
"strings"
)
func Error(args ...interface{}) {
args = append([]interface{}{"ERROR:"}, args...)
_ = log.Default().Output(2, strings.TrimSuffix(fmt.Sprintln(args...), "\n"))
}
func Errorf(format string, args ...interface{}) {
_ = log.Default().Output(2, fmt.Sprintf("ERROR: "+format, args...))
}
func Fatalf(format string, args ...interface{}) {
_ = log.Default().Output(2, fmt.Sprintf("ERROR: "+format, args...))
os.Exit(1)

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +55 to +61
if os.Getenv("LOGGING_FATALF_TEST") == "1" {
log.SetOutput(os.Stderr)
log.SetFlags(0)
log.SetPrefix("")
Fatalf("unable to send email %d", 42)
return
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

TestFatalfAddsPrefixAndExits will take the helper-process branch (and call os.Exit(1)) if LOGGING_FATALF_TEST is already set in the caller’s environment. To make the test robust, explicitly clear/override this env var in the parent process (e.g., via t.Setenv before the getenv check) and only set it to "1" for the exec’d child process.

Copilot uses AI. Check for mistakes.
Comment thread api/internal/app/run.go
statsConn, err := statsd.New(statsd.Address(cfg.StatsdAddress))
if err != nil {
log.Printf("warning: statsd initialization failed: %v", err)
logging.Errorf("statsd initialization failed: %v", err)
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

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

This error is logged but the application continues to run without StatsD (statsConn can be nil). The updated message drops the previous “warning”/non-fatal signal, which can be misleading operationally; consider including explicit wording like “continuing without metrics” (while still keeping the required "ERROR:" prefix).

Suggested change
logging.Errorf("statsd initialization failed: %v", err)
logging.Errorf("ERROR: statsd initialization failed: %v; continuing without metrics", err)

Copilot uses AI. Check for mistakes.
@julsemaan julsemaan merged commit e0e1ff7 into master Apr 15, 2026
14 checks passed
@julsemaan julsemaan deleted the chore/api-error-logs branch April 15, 2026 23:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ensure all API log lines that have errors follow the ERROR: prefix

2 participants