This is an automated email from the ASF dual-hosted git repository. pabloem pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/beam.git
The following commit(s) were added to refs/heads/master by this push: new 38deb59 Merge pull request #16241 from [BEAM-13440] [Playground] Implement initialization of Cloud Logger 38deb59 is described below commit 38deb59f3baf2d54f215ffd7a9900918419ec87b Author: Pavel Avilov <avilovpav...@gmail.com> AuthorDate: Wed Dec 22 01:54:44 2021 +0300 Merge pull request #16241 from [BEAM-13440] [Playground] Implement initialization of Cloud Logger * Implement initialization of Cloud Logger * Edit SetupLogger method; * Edit comments for methods * Refactoring code; Co-authored-by: daria-malkova <daria.malk...@akvelon.com> --- playground/backend/cmd/server/server.go | 3 + .../backend/internal/environment/application.go | 20 +++++- .../internal/environment/application_test.go | 82 ++++++++++++++++++++++ .../internal/environment/environment_service.go | 7 +- .../environment/environment_service_test.go | 21 ++++-- .../internal/logger/cloud_logging_handler.go | 1 + playground/backend/internal/logger/logger.go | 51 +++++++------- playground/backend/internal/logger/logger_test.go | 4 +- playground/backend/internal/logger/std_handler.go | 78 ++++++++++++++++++++ 9 files changed, 233 insertions(+), 34 deletions(-) diff --git a/playground/backend/cmd/server/server.go b/playground/backend/cmd/server/server.go index 143764b..4b47c78 100644 --- a/playground/backend/cmd/server/server.go +++ b/playground/backend/cmd/server/server.go @@ -36,6 +36,9 @@ func runServer() error { if err != nil { return err } + + logger.SetupLogger(ctx, envService.ApplicationEnvs.LaunchSite(), envService.ApplicationEnvs.GoogleProjectId()) + grpcServer := grpc.NewServer() cacheService, err := setupCache(ctx, envService.ApplicationEnvs) diff --git a/playground/backend/internal/environment/application.go b/playground/backend/internal/environment/application.go index 119f812..58665ea 100644 --- a/playground/backend/internal/environment/application.go +++ b/playground/backend/internal/environment/application.go @@ -90,14 +90,22 @@ type ApplicationEnvs struct { // pipelineExecuteTimeout is timeout for code processing pipelineExecuteTimeout time.Duration + + // launchSite is a launch site of application + launchSite string + + // projectId is the Google Сloud project id + projectId string } // NewApplicationEnvs constructor for ApplicationEnvs -func NewApplicationEnvs(workingDir string, cacheEnvs *CacheEnvs, pipelineExecuteTimeout time.Duration) *ApplicationEnvs { +func NewApplicationEnvs(workingDir, launchSite, projectId string, cacheEnvs *CacheEnvs, pipelineExecuteTimeout time.Duration) *ApplicationEnvs { return &ApplicationEnvs{ workingDir: workingDir, cacheEnvs: cacheEnvs, pipelineExecuteTimeout: pipelineExecuteTimeout, + launchSite: launchSite, + projectId: projectId, } } @@ -115,3 +123,13 @@ func (ae *ApplicationEnvs) CacheEnvs() *CacheEnvs { func (ae *ApplicationEnvs) PipelineExecuteTimeout() time.Duration { return ae.pipelineExecuteTimeout } + +// LaunchSite returns launch site of application +func (ae *ApplicationEnvs) LaunchSite() string { + return ae.launchSite +} + +// GoogleProjectId returns Google Сloud project id +func (ae *ApplicationEnvs) GoogleProjectId() string { + return ae.projectId +} diff --git a/playground/backend/internal/environment/application_test.go b/playground/backend/internal/environment/application_test.go index c023fcf..4c6d062 100644 --- a/playground/backend/internal/environment/application_test.go +++ b/playground/backend/internal/environment/application_test.go @@ -260,3 +260,85 @@ func TestApplicationEnvs_PipelineExecuteTimeout(t *testing.T) { }) } } + +func TestApplicationEnvs_LaunchSite(t *testing.T) { + type fields struct { + workingDir string + cacheEnvs *CacheEnvs + pipelineExecuteTimeout time.Duration + launchSite string + googleProjectId string + } + tests := []struct { + name string + fields fields + want string + }{ + { + // Test case with calling LaunchSite method. + // As a result, want to receive an expected launch site. + name: "get launch site", + fields: fields{ + workingDir: "", + cacheEnvs: &CacheEnvs{}, + pipelineExecuteTimeout: 0, + launchSite: "local", + }, + want: "local", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ae := &ApplicationEnvs{ + workingDir: tt.fields.workingDir, + cacheEnvs: tt.fields.cacheEnvs, + pipelineExecuteTimeout: tt.fields.pipelineExecuteTimeout, + launchSite: tt.fields.launchSite, + projectId: tt.fields.googleProjectId, + } + if got := ae.LaunchSite(); got != tt.want { + t.Errorf("LaunchSite() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestApplicationEnvs_GoogleProjectId(t *testing.T) { + type fields struct { + workingDir string + cacheEnvs *CacheEnvs + pipelineExecuteTimeout time.Duration + googleProjectId string + } + tests := []struct { + name string + fields fields + want string + }{ + { + // Test case with calling GoogleProjectId method. + // As a result, want to receive an expected project id. + name: "get google project id", + fields: fields{ + workingDir: "", + cacheEnvs: &CacheEnvs{}, + pipelineExecuteTimeout: 0, + googleProjectId: "MOCK_GOOGLE_PROJECT_ID", + }, + want: "MOCK_GOOGLE_PROJECT_ID", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ae := &ApplicationEnvs{ + workingDir: tt.fields.workingDir, + cacheEnvs: tt.fields.cacheEnvs, + pipelineExecuteTimeout: tt.fields.pipelineExecuteTimeout, + projectId: tt.fields.googleProjectId, + } + if got := ae.GoogleProjectId(); got != tt.want { + t.Errorf("GoogleProjectId() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/playground/backend/internal/environment/environment_service.go b/playground/backend/internal/environment/environment_service.go index 2861618..2a975f2 100644 --- a/playground/backend/internal/environment/environment_service.go +++ b/playground/backend/internal/environment/environment_service.go @@ -41,6 +41,9 @@ const ( cacheKeyExpirationTimeKey = "KEY_EXPIRATION_TIME" pipelineExecuteTimeoutKey = "PIPELINE_EXPIRATION_TIMEOUT" protocolTypeKey = "PROTOCOL_TYPE" + launchSiteKey = "LAUNCH_SITE" + projectIdKey = "GOOGLE_CLOUD_PROJECT" + defaultLaunchSite = "local" defaultProtocol = "HTTP" defaultIp = "localhost" defaultPort = 8080 @@ -90,6 +93,8 @@ func GetApplicationEnvsFromOsEnvs() (*ApplicationEnvs, error) { cacheExpirationTime := defaultCacheKeyExpirationTime cacheType := getEnv(cacheTypeKey, defaultCacheType) cacheAddress := getEnv(cacheAddressKey, defaultCacheAddress) + launchSite := getEnv(launchSiteKey, defaultLaunchSite) + projectId := os.Getenv(projectIdKey) if value, present := os.LookupEnv(cacheKeyExpirationTimeKey); present { if converted, err := time.ParseDuration(value); err == nil { @@ -107,7 +112,7 @@ func GetApplicationEnvsFromOsEnvs() (*ApplicationEnvs, error) { } if value, present := os.LookupEnv(workingDirKey); present { - return NewApplicationEnvs(value, NewCacheEnvs(cacheType, cacheAddress, cacheExpirationTime), pipelineExecuteTimeout), nil + return NewApplicationEnvs(value, launchSite, projectId, NewCacheEnvs(cacheType, cacheAddress, cacheExpirationTime), pipelineExecuteTimeout), nil } return nil, errors.New("APP_WORK_DIR env should be provided with os.env") } diff --git a/playground/backend/internal/environment/environment_service_test.go b/playground/backend/internal/environment/environment_service_test.go index 600e933..cfadce1 100644 --- a/playground/backend/internal/environment/environment_service_test.go +++ b/playground/backend/internal/environment/environment_service_test.go @@ -26,8 +26,8 @@ import ( ) const ( - javaConfig = "{\n \"compile_cmd\": \"javac\",\n \"run_cmd\": \"java\",\n \"test_cmd\": \"java\",\n \"compile_args\": [\n \"-d\",\n \"bin\",\n \"-classpath\"\n ],\n \"run_args\": [\n \"-cp\",\n \"bin:\"\n ],\n \"test_args\": [\n \"-cp\",\n \"bin:\",\n \"org.junit.runner.JUnitCore\"\n ]\n}" - jarsPath = "/opt/apache/beam/jars/*" + javaConfig = "{\n \"compile_cmd\": \"javac\",\n \"run_cmd\": \"java\",\n \"test_cmd\": \"java\",\n \"compile_args\": [\n \"-d\",\n \"bin\",\n \"-classpath\"\n ],\n \"run_args\": [\n \"-cp\",\n \"bin:\"\n ],\n \"test_args\": [\n \"-cp\",\n \"bin:\",\n \"org.junit.runner.JUnitCore\"\n ]\n}" + defaultProjectId = "" ) var executorConfig *ExecutorConfig @@ -90,7 +90,7 @@ func TestNewEnvironment(t *testing.T) { {name: "create env service with default envs", want: &Environment{ NetworkEnvs: *NewNetworkEnvs(defaultIp, defaultPort, defaultProtocol), BeamSdkEnvs: *NewBeamEnvs(defaultSdk, executorConfig, preparedModDir), - ApplicationEnvs: *NewApplicationEnvs("/app", &CacheEnvs{defaultCacheType, defaultCacheAddress, defaultCacheKeyExpirationTime}, defaultPipelineExecuteTimeout), + ApplicationEnvs: *NewApplicationEnvs("/app", defaultLaunchSite, defaultProjectId, &CacheEnvs{defaultCacheType, defaultCacheAddress, defaultCacheKeyExpirationTime}, defaultPipelineExecuteTimeout), }}, } for _, tt := range tests { @@ -98,7 +98,7 @@ func TestNewEnvironment(t *testing.T) { if got := NewEnvironment( *NewNetworkEnvs(defaultIp, defaultPort, defaultProtocol), *NewBeamEnvs(defaultSdk, executorConfig, preparedModDir), - *NewApplicationEnvs("/app", &CacheEnvs{defaultCacheType, defaultCacheAddress, defaultCacheKeyExpirationTime}, defaultPipelineExecuteTimeout)); !reflect.DeepEqual(got, tt.want) { + *NewApplicationEnvs("/app", defaultLaunchSite, defaultProjectId, &CacheEnvs{defaultCacheType, defaultCacheAddress, defaultCacheKeyExpirationTime}, defaultPipelineExecuteTimeout)); !reflect.DeepEqual(got, tt.want) { t.Errorf("NewEnvironment() = %v, want %v", got, tt.want) } }) @@ -205,8 +205,17 @@ func Test_getApplicationEnvsFromOsEnvs(t *testing.T) { wantErr bool envsToSet map[string]string }{ - {name: "working dir is provided", want: NewApplicationEnvs("/app", &CacheEnvs{defaultCacheType, defaultCacheAddress, defaultCacheKeyExpirationTime}, defaultPipelineExecuteTimeout), wantErr: false, envsToSet: map[string]string{workingDirKey: "/app"}}, - {name: "working dir isn't provided", want: nil, wantErr: true}, + { + name: "working dir is provided", + want: NewApplicationEnvs("/app", defaultLaunchSite, defaultProjectId, &CacheEnvs{defaultCacheType, defaultCacheAddress, defaultCacheKeyExpirationTime}, defaultPipelineExecuteTimeout), + wantErr: false, + envsToSet: map[string]string{workingDirKey: "/app", launchSiteKey: defaultLaunchSite, projectIdKey: defaultProjectId}, + }, + { + name: "working dir isn't provided", + want: nil, + wantErr: true, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/playground/backend/internal/logger/cloud_logging_handler.go b/playground/backend/internal/logger/cloud_logging_handler.go index 62afb42..908ab4d 100644 --- a/playground/backend/internal/logger/cloud_logging_handler.go +++ b/playground/backend/internal/logger/cloud_logging_handler.go @@ -23,6 +23,7 @@ import ( const logId = "playground-log" +// CloudLoggingHandler represents 'Cloud Logging' package that logs to Google Cloud Logging service. type CloudLoggingHandler struct { logger *logging.Logger client *logging.Client diff --git a/playground/backend/internal/logger/logger.go b/playground/backend/internal/logger/logger.go index 9a512ea..0ffc574 100644 --- a/playground/backend/internal/logger/logger.go +++ b/playground/backend/internal/logger/logger.go @@ -16,22 +16,43 @@ package logger import ( - "fmt" + "cloud.google.com/go/logging" + "context" "log" ) type Severity string const ( - INFO Severity = "[INFO]:" - WARN Severity = "[WARN]:" - ERROR Severity = "[ERROR]:" - FATAL Severity = "[FATAL]:" - DEBUG Severity = "[DEBUG]:" + INFO Severity = "[INFO]:" + WARN Severity = "[WARN]:" + ERROR Severity = "[ERROR]:" + FATAL Severity = "[FATAL]:" + DEBUG Severity = "[DEBUG]:" + appEngine = "app_engine" ) var handlers []Handler +// SetupLogger constructs logger by application environment +// Add handlers in root logger: +// CloudLoggingHandler - if server running on App Engine +// StdHandler - if server running locally +func SetupLogger(ctx context.Context, launchSite, googleProjectId string) { + switch launchSite { + case appEngine: + client, err := logging.NewClient(ctx, googleProjectId) + if err != nil { + log.Fatalf("Failed to create client: %v", err) + } + cloudLogger := NewCloudLoggingHandler(client) + AddHandler(cloudLogger) + default: + stdLogger := NewStdHandler() + AddHandler(stdLogger) + } +} + // SetHandlers set a new array of logger handlers func SetHandlers(h []Handler) { handlers = h @@ -46,76 +67,58 @@ func Info(args ...interface{}) { for _, handler := range handlers { handler.Info(args...) } - logMessage(INFO, args...) } func Infof(format string, args ...interface{}) { for _, handler := range handlers { handler.Infof(format, args...) } - logMessage(INFO, fmt.Sprintf(format, args...)) } func Warn(args ...interface{}) { for _, handler := range handlers { handler.Warn(args...) } - logMessage(WARN, args...) } func Warnf(format string, args ...interface{}) { for _, handler := range handlers { handler.Warnf(format, args...) } - logMessage(WARN, fmt.Sprintf(format, args...)) } func Error(args ...interface{}) { for _, handler := range handlers { handler.Error(args...) } - logMessage(ERROR, args...) } func Errorf(format string, args ...interface{}) { for _, handler := range handlers { handler.Errorf(format, args...) } - logMessage(ERROR, fmt.Sprintf(format, args...)) } func Debug(args ...interface{}) { for _, handler := range handlers { handler.Debug(args...) } - logMessage(DEBUG, args...) } func Debugf(format string, args ...interface{}) { for _, handler := range handlers { handler.Debugf(format, args...) } - logMessage(DEBUG, fmt.Sprintf(format, args...)) } func Fatal(args ...interface{}) { for _, handler := range handlers { handler.Fatal(args...) } - args = append([]interface{}{FATAL}, args...) - log.Fatalln(args...) } func Fatalf(format string, args ...interface{}) { for _, handler := range handlers { handler.Fatalf(format, args...) } - args = append([]interface{}{FATAL}, fmt.Sprintf(format, args...)) - log.Fatalln(args...) -} - -// logMessage logs a message at level severity. -func logMessage(severity Severity, args ...interface{}) { - args = append([]interface{}{severity}, args...) - log.Println(args...) } diff --git a/playground/backend/internal/logger/logger_test.go b/playground/backend/internal/logger/logger_test.go index 82cc69b..2bc4278 100644 --- a/playground/backend/internal/logger/logger_test.go +++ b/playground/backend/internal/logger/logger_test.go @@ -29,8 +29,8 @@ import ( var preparedHandler testHandler const ( - codeFatal = "package main\n\nimport (\n\t\"beam.apache.org/playground/backend/internal/logger\"\n)\n\nfunc main() {\n\tlogger.Fatal(\"%v\")\n}" - codeFatalf = "package main\n\nimport (\n\t\"beam.apache.org/playground/backend/internal/logger\"\n)\n\nfunc main() {\n\tlogger.Fatalf(\"%v\",\"%s\")\n}" + codeFatal = "package main\n\nimport (\n\t\"beam.apache.org/playground/backend/internal/logger\"\n)\n\nfunc main() {\n\tlogger.AddHandler(logger.StdHandler{})\n\tlogger.Fatal(\"%v\")\n}" + codeFatalf = "package main\n\nimport (\n\t\"beam.apache.org/playground/backend/internal/logger\"\n)\n\nfunc main() {\n\tlogger.AddHandler(logger.StdHandler{})\n\tlogger.Fatalf(\"%v\",\"%s\")\n}" testLoggerDir = "testLogger" testFatalDir = "testFatal" testFatalfDir = "testFatalf" diff --git a/playground/backend/internal/logger/std_handler.go b/playground/backend/internal/logger/std_handler.go new file mode 100644 index 0000000..1534c8f --- /dev/null +++ b/playground/backend/internal/logger/std_handler.go @@ -0,0 +1,78 @@ +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logger + +import ( + "fmt" + "log" +) + +// StdHandler represents standard 'log' package that logs to stderr +type StdHandler struct { +} + +// NewStdHandler creates StdHandler +func NewStdHandler() *StdHandler { + return &StdHandler{} +} + +func (h StdHandler) Info(args ...interface{}) { + h.logMessage(INFO, args...) +} + +func (h StdHandler) Infof(format string, args ...interface{}) { + h.logMessage(INFO, fmt.Sprintf(format, args...)) +} + +func (h StdHandler) Warn(args ...interface{}) { + h.logMessage(WARN, args...) +} + +func (h StdHandler) Warnf(format string, args ...interface{}) { + h.logMessage(WARN, fmt.Sprintf(format, args...)) +} + +func (h StdHandler) Error(args ...interface{}) { + h.logMessage(ERROR, args...) +} + +func (h StdHandler) Errorf(format string, args ...interface{}) { + h.logMessage(ERROR, fmt.Sprintf(format, args...)) +} + +func (h StdHandler) Debug(args ...interface{}) { + h.logMessage(DEBUG, args...) +} + +func (h StdHandler) Debugf(format string, args ...interface{}) { + h.logMessage(DEBUG, fmt.Sprintf(format, args...)) +} + +func (h StdHandler) Fatal(args ...interface{}) { + args = append([]interface{}{FATAL}, args...) + log.Fatalln(args...) +} + +func (h StdHandler) Fatalf(format string, args ...interface{}) { + args = append([]interface{}{FATAL}, fmt.Sprintf(format, args...)) + log.Fatalln(args...) +} + +// logMessage logs a message at level severity. +func (h StdHandler) logMessage(severity Severity, args ...interface{}) { + args = append([]interface{}{severity}, args...) + log.Println(args...) +}