diff --git a/docs/docs/reference/cli.md b/docs/docs/reference/cli.md index 6303eb33..601e61ae 100644 --- a/docs/docs/reference/cli.md +++ b/docs/docs/reference/cli.md @@ -63,6 +63,7 @@ Run a signed executable in an enclave. You can pass arbitrary arguments to the e Environment variables are only readable from within the enclave if they start with `EDG_`. Set OE_SIMULATION=1 to run in simulation mode. +Set EDG_LOG_FORMAT=json to enable JSON-structured logs for EGo's startup code. ``` ego run [args...] @@ -86,6 +87,7 @@ Requires the following configuration environment variables: EDG_MARBLE_UUID_FILE The location where this Marble will store its UUID Set OE_SIMULATION=1 to run in simulation mode. +Set EDG_LOG_FORMAT=json to enable JSON-structured logs for EGo's startup code. ``` ego marblerun diff --git a/ego/ego/cmd/marblerun.go b/ego/ego/cmd/marblerun.go index 1f7920cf..fcb88856 100644 --- a/ego/ego/cmd/marblerun.go +++ b/ego/ego/cmd/marblerun.go @@ -27,7 +27,8 @@ Requires the following configuration environment variables: EDG_MARBLE_DNS_NAMES The alternative DNS names for this Marble's TLS certificate EDG_MARBLE_UUID_FILE The location where this Marble will store its UUID -Set OE_SIMULATION=1 to run in simulation mode.`, +Set OE_SIMULATION=1 to run in simulation mode. +Set EDG_LOG_FORMAT=json to enable JSON-structured logs for EGo's startup code.`, Args: cobra.ExactArgs(1), DisableFlagsInUseLine: true, diff --git a/ego/ego/cmd/run.go b/ego/ego/cmd/run.go index 7e4f9054..263cbf02 100644 --- a/ego/ego/cmd/run.go +++ b/ego/ego/cmd/run.go @@ -20,7 +20,8 @@ func newRunCmd() *cobra.Command { Environment variables are only readable from within the enclave if they start with ` + "`EDG_`" + `. -Set OE_SIMULATION=1 to run in simulation mode.`, +Set OE_SIMULATION=1 to run in simulation mode. +Set EDG_LOG_FORMAT=json to enable JSON-structured logs for EGo's startup code.`, Args: cobra.MinimumNArgs(1), DisableFlagParsing: true, DisableFlagsInUseLine: true, diff --git a/ego/ego/main.go b/ego/ego/main.go index 8f0ae674..b3e42ce8 100644 --- a/ego/ego/main.go +++ b/ego/ego/main.go @@ -7,10 +7,11 @@ package main import ( - "fmt" + "log/slog" "os" "github.com/edgelesssys/ego/ego/ego/cmd" + "github.com/edgelesssys/ego/ego/internal/config" ) // Don't touch! Automatically injected at build-time. @@ -20,7 +21,10 @@ var ( ) func main() { - fmt.Fprintf(os.Stderr, "EGo v%v (%v)\n", version, gitCommit) + if config.LogJSON { + slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stderr, nil))) + } + slog.Info("EGo", "version", version, "git_commit", gitCommit) if cmd.Execute() != nil { os.Exit(1) } diff --git a/ego/go.mod b/ego/go.mod index d1581999..f3c405f3 100644 --- a/ego/go.mod +++ b/ego/go.mod @@ -1,23 +1,23 @@ module github.com/edgelesssys/ego/ego -go 1.20 +go 1.23.3 require ( - github.com/edgelesssys/marblerun v1.4.1 + github.com/edgelesssys/marblerun v1.6.1-0.20241120142109-82ce1218cbc5 github.com/google/go-cmp v0.6.0 - github.com/klauspost/cpuid/v2 v2.2.8 + github.com/klauspost/cpuid/v2 v2.2.9 github.com/spf13/afero v1.11.0 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 - golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e - google.golang.org/grpc v1.64.1 + golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f + google.golang.org/grpc v1.68.0 ) require ( github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/edgelesssys/ego v1.5.2 // indirect - github.com/edgelesssys/era v0.3.3 // indirect + github.com/edgelesssys/ego v1.6.0 // indirect + github.com/go-jose/go-jose/v4 v4.0.4 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -25,12 +25,11 @@ require ( github.com/spf13/pflag v1.0.5 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.28.0 // indirect - golang.org/x/net v0.30.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/text v0.19.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240812133136-8ffd90a71988 // indirect - google.golang.org/protobuf v1.34.2 // indirect - gopkg.in/square/go-jose.v2 v2.6.0 // indirect + golang.org/x/crypto v0.29.0 // indirect + golang.org/x/net v0.31.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.20.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 // indirect + google.golang.org/protobuf v1.35.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/ego/go.sum b/ego/go.sum index a1cd3d12..599bca82 100644 --- a/ego/go.sum +++ b/ego/go.sum @@ -3,20 +3,22 @@ github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/edgelesssys/ego v1.5.2 h1:DC8H073Z49rGT/0aom1yGOHDVtv2PnTX/9HZlB0Fdv8= -github.com/edgelesssys/ego v1.5.2/go.mod h1:TLwP4eAy38XUWuV2E71OYX18RAv4ZJpaLEWQFS751t0= -github.com/edgelesssys/era v0.3.3 h1:EyZvkR/WJceMaV+15lt+CYI0h6mC8pUOFklbbKT8KwQ= -github.com/edgelesssys/era v0.3.3/go.mod h1:yGYrnt8vxxFUsbMQCMwEBItNK9vNqS0f9YONvr78lBY= -github.com/edgelesssys/marblerun v1.4.1 h1:k/v1vWuRaynEJtajYVjLKtK0EGpOVxNhd2AvI/63axs= -github.com/edgelesssys/marblerun v1.4.1/go.mod h1:q+EjAV3OUTHObBpUp7tI774uZCDgVdDEf1vYGi8aXYA= +github.com/edgelesssys/ego v1.6.0 h1:QfArp8vUwsH1G4966ZQv5i3f1BxsbIP2uOKkIGjsGyQ= +github.com/edgelesssys/ego v1.6.0/go.mod h1:XAej1u86vecy/UYGxMxDP37LTHTPC3/7cbqNodwcTXE= +github.com/edgelesssys/marblerun v1.6.1-0.20241120142109-82ce1218cbc5 h1:05n8w5IuIQ1wguJq3sQ6wBFAKiUIHAsO3pEWlctir0Y= +github.com/edgelesssys/marblerun v1.6.1-0.20241120142109-82ce1218cbc5/go.mod h1:bagQD7OTtT8xOWTA5dXLqd5ci3FVeDqTf7SYXiqczzM= +github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= +github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= -github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -30,30 +32,28 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= -golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk= -golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240812133136-8ffd90a71988 h1:V71AcdLZr2p8dC9dbOIMCpqi4EmRl8wUwnJzXXLmbmc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240812133136-8ffd90a71988/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= -google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA= -google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= +golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 h1:LWZqQOEjDyONlF1H6afSWpAL/znlREo2tHfLoe+8LMA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= +google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/ego/internal/config/config.go b/ego/internal/config/config.go new file mode 100644 index 00000000..e269557f --- /dev/null +++ b/ego/internal/config/config.go @@ -0,0 +1,14 @@ +// Copyright (c) Edgeless Systems GmbH. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +package config + +import ( + "os" + "strings" +) + +var LogJSON = strings.EqualFold(os.Getenv("EDG_LOG_FORMAT"), "json") diff --git a/ego/internal/launch/jsonifier.go b/ego/internal/launch/jsonifier.go new file mode 100644 index 00000000..00804702 --- /dev/null +++ b/ego/internal/launch/jsonifier.go @@ -0,0 +1,54 @@ +// Copyright (c) Edgeless Systems GmbH. +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +package launch + +import ( + "bytes" + "io" + "log/slog" + "regexp" +) + +var rxLogTag = regexp.MustCompile(`^\[(erthost|ego)\] `) + +// jsonifier is a writer that rewrites ert/ego log messages to JSON. +type jsonifier struct { + out io.Writer + log *slog.Logger + buf []byte +} + +func newJsonifier(out io.Writer) *jsonifier { + return &jsonifier{ + out: out, + log: slog.New(slog.NewJSONHandler(out, nil)), + } +} + +func (j *jsonifier) Write(p []byte) (int, error) { + buf := append(j.buf, p...) + + // write out all complete lines in buf + for { + idx := bytes.IndexByte(buf, '\n') + if idx == -1 { + break + } + + // use JSON logger if line begins with known tag + if rxLogTag.Match(buf[:idx]) { + j.log.Info(string(buf[:idx])) + } else { + _, _ = j.out.Write(buf[:idx+1]) + } + + buf = buf[idx+1:] + } + + j.buf = buf + return len(p), nil +} diff --git a/ego/internal/launch/launch.go b/ego/internal/launch/launch.go index c8a02f34..c94a016d 100644 --- a/ego/internal/launch/launch.go +++ b/ego/internal/launch/launch.go @@ -11,6 +11,8 @@ import ( "os" "os/exec" "syscall" + + "github.com/edgelesssys/ego/ego/internal/config" ) // run launches an application with a CappedBuffer and also translates potential Edgeless RT / Open Enclave errors into more user-friendly ones. @@ -28,7 +30,11 @@ func run(runner Runner, cmd *exec.Cmd) (int, error) { // capture stdout and stderr var stdout, stderr cappedBuffer - cmd.Stdout = io.MultiWriter(os.Stdout, &stdout) + var osStdout io.Writer = os.Stdout + if config.LogJSON { + osStdout = newJsonifier(osStdout) + } + cmd.Stdout = io.MultiWriter(osStdout, &stdout) cmd.Stderr = io.MultiWriter(os.Stderr, &stderr) if err := runner.Run(cmd); err != nil {