tool/cmd/main.go (138 lines of code) (raw):
// Copyright (c) 2024 Alibaba Group Holding Ltd.
//
// Licensed 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 main
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/alibaba/opentelemetry-go-auto-instrumentation/tool/config"
"github.com/alibaba/opentelemetry-go-auto-instrumentation/tool/errc"
"github.com/alibaba/opentelemetry-go-auto-instrumentation/tool/instrument"
"github.com/alibaba/opentelemetry-go-auto-instrumentation/tool/preprocess"
"github.com/alibaba/opentelemetry-go-auto-instrumentation/tool/util"
)
const (
SubcommandSet = "set"
SubcommandGo = "go"
SubcommandVersion = "version"
SubcommandRemix = "remix"
)
var usage = `Usage: {} <command> [args]
Example:
{} go build
{} go build main.go
{} version
{} set -verbose -rule=custom.json
Command:
version print the version
set set the configuration
go build the Go application
`
func printUsage() {
name, _ := util.GetToolName()
usage = strings.ReplaceAll(usage, "{}", name)
fmt.Print(usage)
}
func initLog() error {
name := util.PPreprocess
path := util.GetTempBuildDirWith(name)
logPath := filepath.Join(path, util.DebugLogFile)
_, err := os.Create(logPath)
if err != nil {
return errc.New(errc.ErrCreateFile, err.Error())
}
return nil
}
func initTempDir() error {
// All temp directories are prepared before, instrument phase should not
// create any new directories.
if util.GetRunPhase() == util.PInstrument {
return nil
}
// Make temp build directory if not exists
if util.PathNotExists(util.TempBuildDir) {
err := os.MkdirAll(util.TempBuildDir, 0777)
if err != nil {
return errc.New(errc.ErrMkdirAll, err.Error())
}
}
for _, subdir := range []string{util.PPreprocess, util.PInstrument} {
if util.PathExists(util.GetTempBuildDirWith(subdir)) {
err := os.RemoveAll(util.GetTempBuildDirWith(subdir))
if err != nil {
return errc.New(errc.ErrRemoveAll, err.Error())
}
}
err := os.MkdirAll(util.GetTempBuildDirWith(subdir), 0777)
if err != nil {
return errc.New(errc.ErrMkdirAll, err.Error())
}
}
return nil
}
func initEnv() error {
util.Assert(len(os.Args) >= 2, "no command specified")
// Determine the run phase
switch {
case strings.HasSuffix(os.Args[1], SubcommandGo):
// otel go build?
util.SetRunPhase(util.PPreprocess)
case os.Args[1] == SubcommandRemix:
// otel remix?
util.SetRunPhase(util.PInstrument)
default:
// do nothing
}
// Create temp build directory
err := initTempDir()
if err != nil {
return err
}
// Create log files under temp build directory
if util.InPreprocess() {
err := initLog()
if err != nil {
return err
}
}
// Prepare shared configuration
if util.InPreprocess() || util.InInstrument() {
err = config.InitConfig()
if err != nil {
return err
}
}
return nil
}
func fatal(err error) {
message := "===== Environments =====\n"
message += fmt.Sprintf("%-11s: %s\n", "Command", strings.Join(os.Args, " "))
message += fmt.Sprintf("%-11s: %s\n", "ErrorLog", util.GetLoggerPath())
message += fmt.Sprintf("%-11s: %s\n", "WorkDir", os.Getenv("PWD"))
message += fmt.Sprintf("%-11s: %s, %s, %s\n", "Toolchain",
runtime.GOOS+"/"+runtime.GOARCH,
runtime.Version(), config.ToolVersion)
message += "===== Fatal Error ======\n"
message += err.Error()
util.LogFatal("\033[31m%s\033[0m", message) // log in red color
}
func main() {
if len(os.Args) < 2 {
printUsage()
os.Exit(0)
}
err := initEnv()
if err != nil {
fatal(err)
}
subcmd := os.Args[1]
switch subcmd {
case SubcommandVersion:
err = config.PrintVersion()
case SubcommandSet:
err = config.Configure()
case SubcommandGo:
err = preprocess.Preprocess()
case SubcommandRemix:
err = instrument.Instrument()
default:
printUsage()
}
if err != nil {
if subcmd != SubcommandRemix {
fatal(err)
} else {
// If error occurs in remix phase, we dont want to decoret the error
// message with the environments, just print the error message, the
// caller(preprocess) phase will decorate instead.
util.LogFatal(err.Error())
}
}
}