Skip to content

feat: Add log filters, event handling, and time validation#767

Open
NishchayRajput wants to merge 3 commits intogoharbor:mainfrom
NishchayRajput:feat/logs-filters
Open

feat: Add log filters, event handling, and time validation#767
NishchayRajput wants to merge 3 commits intogoharbor:mainfrom
NishchayRajput:feat/logs-filters

Conversation

@NishchayRajput
Copy link
Copy Markdown

@NishchayRajput NishchayRajput commented Mar 24, 2026

Description

This PR improves Harbor CLI audit-log usability by adding:

  1. Audit event-types command

    • New command: harbor logs events
  2. Convenience filters for harbor logs

    • Added flags:
      • --operation
      • --resource-type
      • --resource
      • --username
      • --from-time
      • --to-time
    • These are combined into Harbor q query syntax internally.
  3. Query/time validation helpers

    • Added internal query builder to merge existing --query/-q with convenience flags.
    • Added time normalization support for:
      • RFC3339 (2025-01-01T01:02:03Z)
      • YYYY-MM-DD HH:MM:SS
    • Validation:
      • --from-time and --to-time must be provided together.
  4. Unit tests for new behavior

    • Added tests for query building and time normalization/validation paths.

Parent Issue: #737

Type of Change

Please select the relevant type.

  • Bug fix
  • New feature
  • Refactor
  • Documentation update
  • Chore / maintenance

Changes

  • cmd/harbor/root/logs.go
    • Added logs events command
    • Added convenience filter flags and query composition logic
  • cmd/harbor/root/logs_test.go
    • Added tests for buildAuditLogQuery and normalizeAuditTime

@NishchayRajput
Copy link
Copy Markdown
Author

Please drop me suggestions and something I missed.

Signed-off-by: Nishchay Rajput <nishchayr@iitbhilai.ac.in>
Signed-off-by: Nishchay Rajput <nishchayr@iitbhilai.ac.in>
Signed-off-by: Nishchay Rajput <nishchayr@iitbhilai.ac.in>
Copy link
Copy Markdown
Contributor

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

This PR enhances the Harbor CLI audit-log experience by adding a logs events subcommand, introducing convenience filter flags for harbor logs, and implementing helper logic for query building plus time normalization/validation.

Changes:

  • Added harbor logs events to list supported Harbor audit log event types (with optional client-side pagination and JSON output support).
  • Added convenience filter flags to harbor logs and composed them into Harbor q query syntax.
  • Added helper functions for audit-log query building, time normalization, and pagination summary output, plus unit tests for these helpers.

Reviewed changes

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

File Description
cmd/harbor/root/logs.go Adds logs events, convenience filter flags, query/time helpers, and pagination summary printing.
cmd/harbor/root/logs_test.go Adds unit tests for query building, time normalization, and event-type pagination/name helpers.
Comments suppressed due to low confidence (1)

cmd/harbor/root/logs.go:103

  • In --follow mode, buildAuditLogQuery is executed once before entering the polling loop. When --from-time is provided without --to-time, the code bakes in an op_time upper bound of “now” at startup, which prevents any newer audit logs from ever matching in subsequent polls. Consider requiring both times, or rebuilding the query (updating the to bound) on each refresh when following.
			query, err := buildAuditLogQuery(
				opts.Q,
				operationFilter,
				resourceTypeFilter,
				resourceFilter,
				usernameFilter,
				fromTimeFilter,
				toTimeFilter,
			)
			if err != nil {
				return err
			}
			opts.Q = query

			if follow {
				var interval time.Duration = 5 * time.Second
				if refreshInterval != "" {
					interval, err = time.ParseDuration(refreshInterval)
					if err != nil {
						return fmt.Errorf("invalid refresh interval: %w", err)
					}
					if interval < 500*time.Millisecond {
						return fmt.Errorf("refresh-interval must be at least 500ms (got: %v)", interval)
					}
				}
				followLogs(opts, interval)
				return nil
			}

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

Comment on lines +235 to +260
func printAuditLogEventTypesTable(eventTypes []*models.AuditLogEventType, page, pageSize int64, total int, showPaginationSummary bool) {
if len(eventTypes) == 0 {
if showPaginationSummary {
fmt.Println("No audit log event types found for the requested page.")
return
}
fmt.Println("No audit log event types found.")
return
}

fmt.Printf("%-6s %-40s\n", "INDEX", "EVENT_TYPE")
fmt.Printf("%-6s %-40s\n", "-----", "----------------------------------------")

startIndex := int64(1)
if showPaginationSummary {
startIndex = (page-1)*pageSize + 1
}
for i, eventType := range eventTypes {
fmt.Printf("%-6d %-40s\n", startIndex+int64(i), auditLogEventTypeName(eventType))
}

if showPaginationSummary {
endIndex := startIndex + int64(len(eventTypes)) - 1
fmt.Printf("\nShowing %d-%d of %d\n", startIndex, endIndex, total)
}
}
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

This command prints a custom table via fmt.Printf directly from the root command. In this repo, list-style outputs are typically implemented as views under pkg/views/* using tablelist.NewModel (e.g. pkg/views/logs/view.go) for consistent formatting and behavior. Consider moving the event-types table rendering into a view (and using the shared table component) to keep output consistent and reduce hand-formatted column maintenance.

Copilot uses AI. Check for mistakes.
Comment on lines +329 to +344
// --to-time alone is not allowed; if provided, --from-time must also be present
if from == "" && to != "" {
return "", fmt.Errorf("--to-time cannot be used without --from-time")
}

// If --from-time is present, use it with either provided --to-time or default to current time
if from != "" {
normalizedFrom, err := normalizeAuditTime(from)
if err != nil {
return "", fmt.Errorf("invalid --from-time: %w", err)
}

normalizedTo := to
if to == "" {
normalizedTo = time.Now().Format("2006-01-02 15:04:05")
} else {
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

PR description says --from-time and --to-time must be provided together, but the implementation allows --from-time without --to-time and defaults the end of the range to time.Now(). Please either enforce the “both together” validation here, or update the PR description/help text/tests to match the intended behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +358 to +367
func normalizeAuditTime(input string) (string, error) {
layouts := []string{
time.RFC3339,
"2006-01-02 15:04:05",
}

for _, layout := range layouts {
if parsed, err := time.Parse(layout, input); err == nil {
return parsed.Format("2006-01-02 15:04:05"), nil
}
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

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

normalizeAuditTime claims to accept RFC3339, but using only time.RFC3339 rejects valid RFC3339 timestamps that include fractional seconds (e.g. 2025-01-01T01:02:03.123Z). Consider adding time.RFC3339Nano (and a unit test) so valid RFC3339 inputs don’t fail unexpectedly.

Copilot uses AI. Check for mistakes.
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.

3 participants