Skip to content

Commit 14c9801

Browse files
KnisterPetercplee
andauthored
feat: add json logger output (#1026)
* feat: add json logger output This will allow to format log output as json. This is helpful in cases where act is not executed on a 'local' machine. * refactor: use runner config Using the runner config to configure logging is cleaner. Co-authored-by: Casey Lee <[email protected]>
1 parent 7d403b8 commit 14c9801

File tree

5 files changed

+62
-23
lines changed

5 files changed

+62
-23
lines changed

cmd/input.go

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type Input struct {
3838
autoRemove bool
3939
artifactServerPath string
4040
artifactServerPort string
41+
jsonLogger bool
4142
}
4243

4344
func (i *Input) resolve(path string) string {

cmd/root.go

+6
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ func Execute(ctx context.Context, version string) {
6060
rootCmd.PersistentFlags().BoolVarP(&input.noWorkflowRecurse, "no-recurse", "", false, "Flag to disable running workflows from subdirectories of specified path in '--workflows'/'-W' flag")
6161
rootCmd.PersistentFlags().StringVarP(&input.workdir, "directory", "C", ".", "working directory")
6262
rootCmd.PersistentFlags().BoolP("verbose", "v", false, "verbose output")
63+
rootCmd.PersistentFlags().BoolVar(&input.jsonLogger, "json", false, "Output logs in json format")
6364
rootCmd.PersistentFlags().BoolVarP(&input.noOutput, "quiet", "q", false, "disable logging of output from steps")
6465
rootCmd.PersistentFlags().BoolVarP(&input.dryrun, "dryrun", "n", false, "dryrun mode")
6566
rootCmd.PersistentFlags().StringVarP(&input.secretfile, "secret-file", "", ".secrets", "file with list of secrets to read from (e.g. --secret-file .secrets)")
@@ -156,6 +157,10 @@ func readEnvs(path string, envs map[string]string) bool {
156157
//nolint:gocyclo
157158
func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []string) error {
158159
return func(cmd *cobra.Command, args []string) error {
160+
if input.jsonLogger {
161+
log.SetFormatter(&log.JSONFormatter{})
162+
}
163+
159164
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" && input.containerArchitecture == "" {
160165
l := log.New()
161166
l.SetFormatter(&log.TextFormatter{
@@ -266,6 +271,7 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
266271
Workdir: input.Workdir(),
267272
BindWorkdir: input.bindWorkdir,
268273
LogOutput: !input.noOutput,
274+
JSONLogger: input.jsonLogger,
269275
Env: envs,
270276
Secrets: secrets,
271277
InsecureSecrets: input.insecureSecrets,

pkg/runner/command_test.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,14 @@ func TestAddmaskUsemask(t *testing.T) {
157157

158158
a := assert.New(t)
159159

160+
config := &Config{
161+
Secrets: map[string]string{},
162+
InsecureSecrets: false,
163+
}
164+
160165
re := captureOutput(t, func() {
161166
ctx := context.Background()
162-
ctx = WithJobLogger(ctx, "testjob", map[string]string{}, false, &rc.Masks)
167+
ctx = WithJobLogger(ctx, "testjob", config, &rc.Masks)
163168

164169
handler := rc.commandHandler(ctx)
165170
handler("::add-mask::secret\n")

pkg/runner/logger.go

+47-21
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,22 @@ func init() {
3838
}
3939

4040
// WithJobLogger attaches a new logger to context that is aware of steps
41-
func WithJobLogger(ctx context.Context, jobName string, secrets map[string]string, insecureSecrets bool, masks *[]string) context.Context {
41+
func WithJobLogger(ctx context.Context, jobName string, config *Config, masks *[]string) context.Context {
4242
mux.Lock()
4343
defer mux.Unlock()
44-
formatter := new(stepLogFormatter)
45-
formatter.color = colors[nextColor%len(colors)]
46-
formatter.secrets = secrets
47-
formatter.insecureSecrets = insecureSecrets
48-
formatter.masks = masks
44+
45+
var formatter logrus.Formatter
46+
if config.JSONLogger {
47+
formatter = &jobLogJSONFormatter{
48+
formatter: &logrus.JSONFormatter{},
49+
masker: valueMasker(config.InsecureSecrets, config.Secrets, masks),
50+
}
51+
} else {
52+
formatter = &jobLogFormatter{
53+
color: colors[nextColor%len(colors)],
54+
masker: valueMasker(config.InsecureSecrets, config.Secrets, masks),
55+
}
56+
}
4957

5058
nextColor++
5159

@@ -64,30 +72,39 @@ func WithJobLogger(ctx context.Context, jobName string, secrets map[string]strin
6472
return common.WithLogger(ctx, rtn)
6573
}
6674

67-
type stepLogFormatter struct {
68-
color int
69-
secrets map[string]string
70-
insecureSecrets bool
71-
masks *[]string
72-
}
75+
type entryProcessor func(entry *logrus.Entry) *logrus.Entry
7376

74-
func (f *stepLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
75-
b := &bytes.Buffer{}
77+
func valueMasker(insecureSecrets bool, secrets map[string]string, masks *[]string) entryProcessor {
78+
return func(entry *logrus.Entry) *logrus.Entry {
79+
if insecureSecrets {
80+
return entry
81+
}
7682

77-
// Replace any secrets in the entry if insecure-secrets flag is not used
78-
if !f.insecureSecrets {
79-
for _, v := range f.secrets {
83+
for _, v := range secrets {
8084
if v != "" {
8185
entry.Message = strings.ReplaceAll(entry.Message, v, "***")
8286
}
8387
}
8488

85-
for _, v := range *f.masks {
89+
for _, v := range *masks {
8690
if v != "" {
8791
entry.Message = strings.ReplaceAll(entry.Message, v, "***")
8892
}
8993
}
94+
95+
return entry
9096
}
97+
}
98+
99+
type jobLogFormatter struct {
100+
color int
101+
masker entryProcessor
102+
}
103+
104+
func (f *jobLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
105+
b := &bytes.Buffer{}
106+
107+
entry = f.masker(entry)
91108

92109
if f.isColored(entry) {
93110
f.printColored(b, entry)
@@ -99,7 +116,7 @@ func (f *stepLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
99116
return b.Bytes(), nil
100117
}
101118

102-
func (f *stepLogFormatter) printColored(b *bytes.Buffer, entry *logrus.Entry) {
119+
func (f *jobLogFormatter) printColored(b *bytes.Buffer, entry *logrus.Entry) {
103120
entry.Message = strings.TrimSuffix(entry.Message, "\n")
104121
jobName := entry.Data["job"]
105122

@@ -112,7 +129,7 @@ func (f *stepLogFormatter) printColored(b *bytes.Buffer, entry *logrus.Entry) {
112129
}
113130
}
114131

115-
func (f *stepLogFormatter) print(b *bytes.Buffer, entry *logrus.Entry) {
132+
func (f *jobLogFormatter) print(b *bytes.Buffer, entry *logrus.Entry) {
116133
entry.Message = strings.TrimSuffix(entry.Message, "\n")
117134
jobName := entry.Data["job"]
118135

@@ -125,7 +142,7 @@ func (f *stepLogFormatter) print(b *bytes.Buffer, entry *logrus.Entry) {
125142
}
126143
}
127144

128-
func (f *stepLogFormatter) isColored(entry *logrus.Entry) bool {
145+
func (f *jobLogFormatter) isColored(entry *logrus.Entry) bool {
129146
isColored := checkIfTerminal(entry.Logger.Out)
130147

131148
if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" {
@@ -147,3 +164,12 @@ func checkIfTerminal(w io.Writer) bool {
147164
return false
148165
}
149166
}
167+
168+
type jobLogJSONFormatter struct {
169+
masker entryProcessor
170+
formatter *logrus.JSONFormatter
171+
}
172+
173+
func (f *jobLogJSONFormatter) Format(entry *logrus.Entry) ([]byte, error) {
174+
return f.formatter.Format(f.masker(entry))
175+
}

pkg/runner/runner.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type Config struct {
3131
ForcePull bool // force pulling of the image, even if already present
3232
ForceRebuild bool // force rebuilding local docker image action
3333
LogOutput bool // log the output from docker run
34+
JSONLogger bool // use json or text logger
3435
Env map[string]string // env for containers
3536
Secrets map[string]string // list of secrets
3637
InsecureSecrets bool // switch hiding output when printing to terminal
@@ -164,7 +165,7 @@ func (runner *runnerImpl) NewPlanExecutor(plan *model.Plan) common.Executor {
164165
}
165166

166167
return nil
167-
})(common.WithJobErrorContainer(WithJobLogger(ctx, jobName, rc.Config.Secrets, rc.Config.InsecureSecrets, &rc.Masks)))
168+
})(common.WithJobErrorContainer(WithJobLogger(ctx, jobName, rc.Config, &rc.Masks)))
168169
})
169170
}
170171
pipeline = append(pipeline, common.NewParallelExecutor(maxParallel, stageExecutor...))

0 commit comments

Comments
 (0)