Selaa lähdekoodia

Merge branch 'dev-app' into dev

Carl 1 vuosi sitten
vanhempi
säilyke
06607bfd82
100 muutettua tiedostoa jossa 8857 lisäystä ja 962 poistoa
  1. 2 4
      Makefile
  2. 1 1
      cgroup/cgroup.go
  3. 1 1
      cgroup/io.go
  4. 1 1
      cgroup/utils.go
  5. 29 0
      common/filter.go
  6. 1 1
      common/net.go
  7. 68 0
      containers/apm_register_app.go
  8. 56 0
      containers/apm_white_list.go
  9. 1 1
      containers/cilium.go
  10. 1 1
      containers/conntrack.go
  11. 95 90
      containers/container.go
  12. 181 85
      containers/container_apm.go
  13. 1 1
      containers/containerd.go
  14. 1 1
      containers/crio.go
  15. 1 1
      containers/dotnet.go
  16. 1 1
      containers/jvm.go
  17. 1 1
      containers/l7.go
  18. 18 2
      containers/process.go
  19. 135 26
      containers/registry.go
  20. 21 0
      containers/registry_apm.go
  21. 26 29
      containers/stack.go
  22. 18 17
      containers/util.go
  23. 4 0
      dist/.gitignore
  24. 87 0
      dist/README.md
  25. 238 0
      dist/package_dir/bin/agentctl
  26. 6 0
      dist/package_dir/bin/start.sh
  27. 3 0
      dist/package_dir/bin/stop.sh
  28. 0 0
      dist/package_dir/conf/.gitkeep
  29. 2926 0
      dist/scripts/install_temp.sh
  30. 611 0
      dist/scripts/package.sh
  31. 1111 0
      dist/scripts/uninstall.sh
  32. BIN
      dist/scripts/xzdec
  33. 1 0
      ebpftracer/ebpf/include/socket_trace_common.h
  34. 2 1
      ebpftracer/ebpf/utrace/go/net/client.probe.bpf.c
  35. 2 1
      ebpftracer/ebpf/utrace/java/net/client.probe.bpf.c
  36. 1 1
      ebpftracer/init.go
  37. 9 12
      ebpftracer/jvm.go
  38. 12 14
      ebpftracer/netcore.go
  39. 9 10
      ebpftracer/stack.go
  40. 71 66
      ebpftracer/tls.go
  41. 34 40
      ebpftracer/tracer.go
  42. 1 1
      ebpftracer/tracer/allocate.go
  43. 4 3
      ebpftracer/tracer/btf_vmlinux.go
  44. 6 8
      ebpftracer/tracer/filter.go
  45. 9 7
      ebpftracer/tracer/inject/inject_linux_amd64.go
  46. 10 8
      ebpftracer/tracer/offset.go
  47. 2 2
      ebpftracer/tracer/ptrace/ptrace_linux.go
  48. 34 34
      ebpftracer/tracer/socket.go
  49. 57 3
      flags/flags.go
  50. 13 5
      go.mod
  51. 16 0
      go.sum
  52. 1 1
      logs/journald_reader.go
  53. 181 0
      logs/log.go
  54. 1 1
      logs/otel.go
  55. 1 1
      logs/tail_reader.go
  56. 38 19
      logs/tail_reader_test.go
  57. 1 1
      node/collector.go
  58. 1 1
      node/disk.go
  59. 1 1
      node/memory.go
  60. 1 1
      node/metadata/aws.go
  61. 1 1
      node/metadata/azure.go
  62. 1 1
      node/metadata/digital_ocean.go
  63. 1 1
      node/metadata/gcp.go
  64. 1 1
      node/metadata/hetzner.go
  65. 1 1
      node/metadata/metadata.go
  66. 1 1
      pinger/pinger.go
  67. 5 5
      pkg/go.opentelemetry.io/otel/example/otel-collector/go.mod
  68. 5 0
      pkg/go.opentelemetry.io/otel/example/otel-collector/go.sum
  69. 187 197
      pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/apm_exporter.go
  70. 48 14
      pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/go.mod
  71. 156 18
      pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/go.sum
  72. 5 5
      pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/go.mod
  73. 5 0
      pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/go.sum
  74. 3 3
      pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/client_apm.go
  75. 23 2
      pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/go.mod
  76. 141 23
      pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/go.sum
  77. 23 0
      pkg/lumberjack/.gitignore
  78. 6 0
      pkg/lumberjack/.travis.yml
  79. 21 0
      pkg/lumberjack/LICENSE
  80. 179 0
      pkg/lumberjack/README.md
  81. 11 0
      pkg/lumberjack/chown.go
  82. 19 0
      pkg/lumberjack/chown_linux.go
  83. 19 0
      pkg/lumberjack/example_test.go
  84. 7 0
      pkg/lumberjack/go.mod
  85. 205 0
      pkg/lumberjack/linux_test.go
  86. 562 0
      pkg/lumberjack/lumberjack.go
  87. 816 0
      pkg/lumberjack/lumberjack_test.go
  88. 27 0
      pkg/lumberjack/rotate_test.go
  89. 91 0
      pkg/lumberjack/testing_test.go
  90. 18 0
      proc/proc.go
  91. 1 1
      profiling/profiling.go
  92. 1 1
      prom/agent.go
  93. 1 1
      prom/wrappers.go
  94. 11 7
      tracing/apm_tracing.go
  95. 3 3
      tracing/tracing.go
  96. 28 0
      utils/enums/enums.go
  97. 26 159
      utils/id.go
  98. 2 1
      utils/kernel.go
  99. 22 0
      utils/modelse/app_info.go
  100. 9 10
      utils/modelse/bpf_struct.go

+ 2 - 4
Makefile

@@ -18,7 +18,7 @@ endif
 all: c-build go-build
 
 build:
-	CGO_ENABLED=1 go build -gcflags="all=-N -l" -buildvcs=false -o euspace
+	CGO_ENABLED=1 go build -gcflags="all=-N -l" -buildvcs=false -o dist/package_dir/bin/euspace
 c:
 	docker exec -it 62d0676aa0b7 sh -c 'cd /opt/github/euspace/ebpftracer && make all ${PARAMS}'
 c-build: c
@@ -28,6 +28,4 @@ go-build:
 go: go-build run
 
 run:
-	ssh [email protected] 'cd /opt/github/euspace && TRACES_ENDPOINT=http://10.0.12.192:18080/docp/api/v2/data/receive ${FILTER} ./euspace --listen="0.0.0.0:8123"'
-send_run:
-	ssh [email protected] 'cd /opt/github/euspace && SEND=1 TRACES_ENDPOINT=http://10.0.12.192:18080/docp/api/v2/data/receive ${FILTER} ./euspace --listen="0.0.0.0:8123"'
+	ssh [email protected] 'cd /opt/github/euspace && CONFIG_ENDPOINT=10.0.16.250:18080 && TRACES_ENDPOINT=http://10.0.16.250:18080/docp/api/v2/data/receive ${FILTER} ./euspace --listen="0.0.0.0:8123"'

+ 1 - 1
cgroup/cgroup.go

@@ -10,7 +10,7 @@ import (
 
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/flags"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 var (

+ 1 - 1
cgroup/io.go

@@ -6,7 +6,7 @@ import (
 	"strconv"
 	"strings"
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 type IOStat struct {

+ 1 - 1
cgroup/utils.go

@@ -5,7 +5,7 @@ import (
 	"strconv"
 	"strings"
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 func readVariablesFromFile(filePath string) (map[string]uint64, error) {

+ 29 - 0
common/filter.go

@@ -0,0 +1,29 @@
+package common
+
+import (
+	"os"
+	"strconv"
+)
+
+var FILTER_PID_KEY = "FILTER_PID"
+
+var INT_FILTER_PID = 0
+
+func init() {
+	filterPid, err := strconv.ParseInt(os.Getenv(FILTER_PID_KEY), 10, 64)
+	if err == nil {
+		INT_FILTER_PID = int(filterPid)
+	}
+}
+
+func IsOpenFilter() bool {
+	return INT_FILTER_PID != 0
+}
+
+func GetFilterPid() (int, bool) {
+	return INT_FILTER_PID, INT_FILTER_PID == 0
+}
+
+func IsFilterPid(filterPid uint32) bool {
+	return int(filterPid) == INT_FILTER_PID
+}

+ 1 - 1
common/net.go

@@ -5,8 +5,8 @@ import (
 	"strings"
 
 	"github.com/coroot/coroot-node-agent/flags"
+	klog "github.com/sirupsen/logrus"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 
 var (

+ 68 - 0
containers/apm_register_app.go

@@ -0,0 +1,68 @@
+package containers
+
+import (
+	"fmt"
+	"github.com/coroot/coroot-node-agent/utils"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
+	klog "github.com/sirupsen/logrus"
+	"time"
+)
+
+func (c *Container) RegisterAppInfo(r *Registry, pid uint32) error {
+	if c.WhiteSettingInfo.AppName == "" {
+		return fmt.Errorf("appname is empty.")
+	}
+	var err error
+	originAppName := c.AppInfo.AppName
+	whiteAppName := c.WhiteSettingInfo.AppName
+	if originAppName != whiteAppName {
+		if c.AppInfo.CodeType.IsUnknownCode() || c.AppInfo.CodeType.IsWaitCheck() {
+			return fmt.Errorf("unknown app code")
+		}
+		//c.AppInfo.AppId, err = utils.BuildInt64ID(whiteAppName).ToInt64()
+		c.AppInfo.AppIdHash.IntVal, err = utils.BuildInt64ID(whiteAppName).ToInt64()
+		c.AppInfo.AppIdHash.HashtVal = utils.BuildInt64ID(whiteAppName).ToHashByte()
+		if err != nil {
+			return err
+		}
+		c.AppInfo.ServiceName = c.AppInfo.CodeType.ServiceTypeString()
+		// 注册
+		hostId, _ := utils.GetHostID()
+		registerAppReq := RegisterAppReq{
+			AppId:   c.AppInfo.AppIdHash.IntVal,
+			AppName: c.WhiteSettingInfo.AppName,
+			// todo AccountId
+			AccountId:   utils.GetAccountID(),
+			AgentId:     c.AppInfo.AgentId,
+			Sn:          c.AppInfo.Sn,
+			Sport:       c.AppInfo.Sport,
+			ServiceType: c.AppInfo.ServiceName,
+			CodeType:    c.AppInfo.CodeType.Int(),
+			App_type:    1,
+			HostId:      hostId,
+		}
+		klog.Infof("[register] Register App: %s.", registerAppReq.String())
+		err = r.connServer.RegisterApp(registerAppReq)
+		if err != nil {
+			klog.WithError(err).Errorf("[register] Failed Register App %s.", registerAppReq.String())
+			return err
+		}
+
+		c.AppInfo.AppName = c.WhiteSettingInfo.AppName
+
+		if originAppName == "" {
+			c.AppInfo.RegisterAt = time.Now().Unix()
+		}
+
+		if originAppName != "" && c.checkL7AttachReady() {
+			klog.Infoln("[kprobe] reset kernel proc_info.")
+			err = r.tracer.InitKProcInfo(pid, &c.AppInfo)
+			if err != nil {
+				klog.WithError(err).Errorf("[kprobe] Failed reset KProcInfo.")
+				return err
+			}
+			c.AppInfo.UpdateAt = time.Now().Unix()
+		}
+	}
+	return nil
+}

+ 56 - 0
containers/apm_white_list.go

@@ -0,0 +1,56 @@
+package containers
+
+import (
+	"github.com/coroot/coroot-node-agent/common"
+	"github.com/coroot/coroot-node-agent/utils"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
+	log "github.com/sirupsen/logrus"
+)
+
+type WhiteListMap map[string][]WhiteSettingInfo
+
+func (r *Registry) getWhiteListByCodeType(codeType CodeType) []WhiteSettingInfo {
+	return r.whiteListRules[codeType.WhiteCodeString()]
+}
+
+func (r *Registry) setWhiteList(whiteData WhiteData) {
+	whiteListMap := make(WhiteListMap)
+	for _, scop := range whiteData.Appscope {
+		code := scop.Code
+		for _, setting := range scop.Settings {
+			whiteListMap[code] = append(whiteListMap[code], setting)
+		}
+	}
+	r.whiteListRules = whiteListMap
+}
+
+func (r *Registry) getWhiteList() (bool, error) {
+	if common.IsOpenFilter() {
+		return false, nil
+	}
+	//hostId, _ := utils.GetHostID()
+	//fmt.Println(hostId)
+	whiteListReq := WhiteListReq{
+		HostId:    10154813500555812,
+		AccountId: utils.GetAccountID(),
+	}
+	whiteData, err := r.connServer.WhiteList(whiteListReq)
+
+	if err != nil {
+		log.Errorf("report WhiteList info error is %v.", err)
+		return false, err
+	}
+
+	//fmt.Println(r.whiteLastUpdatedTime)
+	//fmt.Println(whiteData.LastUpdatedTime)
+	// 不用更新
+	if r.whiteLastUpdatedTime >= 0 && r.whiteLastUpdatedTime == whiteData.LastUpdatedTime {
+		return false, nil
+	}
+
+	// 更新时间
+	r.whiteLastUpdatedTime = whiteData.LastUpdatedTime
+
+	r.setWhiteList(whiteData)
+	return true, nil
+}

+ 1 - 1
containers/cilium.go

@@ -11,8 +11,8 @@ import (
 	"github.com/cilium/cilium/pkg/tuple"
 	"github.com/cilium/cilium/pkg/u8proto"
 	"github.com/coroot/coroot-node-agent/proc"
+	klog "github.com/sirupsen/logrus"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 
 var (

+ 1 - 1
containers/conntrack.go

@@ -5,9 +5,9 @@ import (
 
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/florianl/go-conntrack"
+	klog "github.com/sirupsen/logrus"
 	"github.com/vishvananda/netns"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 
 type Conntrack struct {

+ 95 - 90
containers/container.go

@@ -2,15 +2,12 @@ package containers
 
 import (
 	debugelf "debug/elf"
-	"fmt"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
 	"os"
-	"strconv"
 	"strings"
 	"sync"
 	"time"
 
-	"github.com/coroot/coroot-node-agent/utils"
-
 	"github.com/coroot/coroot-node-agent/cgroup"
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/ebpftracer"
@@ -24,13 +21,13 @@ import (
 	"github.com/coroot/coroot-node-agent/tracing"
 	"github.com/coroot/logparser"
 	"github.com/prometheus/client_golang/prometheus"
+	klog "github.com/sirupsen/logrus"
 	"github.com/vishvananda/netns"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 
 var (
-	gcInterval  = 1 * time.Minute
+	gcInterval  = 10 * time.Second
 	pingTimeout = 300 * time.Millisecond
 )
 
@@ -150,11 +147,17 @@ type Container struct {
 
 	done chan struct{}
 
-	traceMap   map[uint64]*tracing.Trace
-	instanceID utils.ID
-	Symbols    []debugelf.Symbol
-	Uprobes    []tracer.Uprobe
-	UprobesMap map[string]tracer.Uprobe
+	traceMap map[uint64]*tracing.Trace
+	//instanceID   utils.ID
+	Symbols      []debugelf.Symbol
+	Uprobes      []tracer.Uprobe
+	UprobesMap   map[string]tracer.Uprobe
+	l7EventReady bool
+	l7Attach     bool
+	// 白名单详情
+	WhiteSettingInfo WhiteSettingInfo
+	// 应用详情
+	AppInfo AppInfo
 }
 
 func NewContainer(id ContainerID, cg *cgroup.Cgroup, md *ContainerMetadata, hostConntrack *Conntrack, pid uint32) (*Container, error) {
@@ -596,21 +599,13 @@ func (c *Container) onConnectionClose(srcDst AddrPair) bool {
 
 func (c *Container) onDNSRequest(r *l7.RequestData) (map[netaddr.IP]string, string, string) {
 	status := r.Status.DNS()
-	fmt.Println("status:", status)
 	if status == "" {
 		return nil, "", ""
 	}
 	t, fqdn, ips := l7.ParseDns(r.Payload)
-	fmt.Println("t:", t)
-	fmt.Println("fqdn:", fqdn)
-	fmt.Println("ips:", ips)
-
 	if t == "" {
 		return nil, "", ""
 	}
-	fmt.Println("c.dnsStats.Requests", c.dnsStats.Requests)
-	fmt.Println("r.Duration", r.Duration)
-
 	if c.dnsStats.Requests == nil {
 		dnsReq := L7Requests[l7.ProtocolDNS]
 		c.dnsStats.Requests = prometheus.NewCounterVec(
@@ -906,7 +901,7 @@ func (c *Container) runLogParser(logPath string) {
 			parser.Stop()
 			return
 		}
-		klog.InfoS("started varlog logparser", "cg", c.cgroup.Id, "log", logPath)
+		klog.Infoln("started varlog logparser", "cg", c.cgroup.Id, "log", logPath)
 		c.logParsers[logPath] = &LogParser{parser: parser, stop: reader.Stop}
 		return
 	}
@@ -922,7 +917,7 @@ func (c *Container) runLogParser(logPath string) {
 		stop := func() {
 			JournaldUnsubscribe(c.cgroup)
 		}
-		klog.InfoS("started journald logparser", "cg", c.cgroup.Id)
+		klog.Infoln("started journald logparser", "cg", c.cgroup.Id)
 		c.logParsers["journald"] = &LogParser{parser: parser, stop: stop}
 
 	case cgroup.ContainerTypeDocker, cgroup.ContainerTypeContainerd, cgroup.ContainerTypeCrio:
@@ -941,7 +936,7 @@ func (c *Container) runLogParser(logPath string) {
 			parser.Stop()
 			return
 		}
-		klog.InfoS("started container logparser", "cg", c.cgroup.Id)
+		klog.Infoln("started container logparser", "cg", c.cgroup.Id)
 		c.logParsers["stdout/stderr"] = &LogParser{parser: parser, stop: reader.Stop}
 	}
 }
@@ -1083,7 +1078,7 @@ func (c *Container) revalidateListens(now time.Time, actualListens map[netaddr.I
 			if !found {
 				continue
 			}
-			klog.Warningln("missing listen found:", addr, pid)
+			//klog.Warningln("missing listen found:", addr, pid)
 			c.onListenOpen(pid, addr, true)
 		}
 	}
@@ -1100,30 +1095,37 @@ func (c *Container) revalidateListens(now time.Time, actualListens map[netaddr.I
 	}
 }
 
-func (c *Container) attachUprobes(tracer *ebpftracer.Tracer, pid uint32) {
+func (c *Container) attachUprobes(tracer *ebpftracer.Tracer, pid uint32) error {
 	if tracer.DisableL7Tracing() {
-		return
+		return nil
 	}
 	codeType := c.GetCodeTypeFromCache(pid)
 	if codeType.IsUnknownCode() {
-		return
+		return nil
 	}
+	var err error
 	switch codeType {
-	case common.CodeTypeJava:
-		c.attachJVMUprobes(tracer, pid)
-	case common.CodeTypeJavaAot:
-		c.attachJVMUprobes(tracer, pid)
-	case common.CodeTypeGo:
-		c.attachTlsUprobes(tracer, pid)
-	case common.CodeTypeNetCoreAot:
-		c.attachNetCoreUprobes(tracer, pid)
+	case CodeTypeJava:
+		err = c.attachJVMUprobes(tracer, pid)
+	case CodeTypeJavaAot:
+		err = c.attachJVMUprobes(tracer, pid)
+	case CodeTypeGo:
+		err = c.attachTlsUprobes(tracer, pid)
+	case CodeTypeNetCoreAot:
+		err = c.attachNetCoreUprobes(tracer, pid)
 	}
+	if err != nil {
+		c.errorClose(pid)
+		return err
+	}
+	c.l7AttachSuccess()
+	return nil
 }
 
-func (c *Container) GetCodeTypeFromCache(pid uint32) common.CodeType {
+func (c *Container) GetCodeTypeFromCache(pid uint32) CodeType {
 	p := c.processes[pid]
 	if p == nil {
-		return common.CodeTypeUnknown
+		return CodeTypeUnknown
 	}
 	if p.codeType.IsWaitCheck() {
 		p.codeType = GetExeType(pid)
@@ -1131,96 +1133,99 @@ func (c *Container) GetCodeTypeFromCache(pid uint32) common.CodeType {
 	return p.codeType
 }
 
-func (c *Container) attachTlsUprobes(tracer *ebpftracer.Tracer, pid uint32) {
+func (c *Container) attachTlsUprobes(tracer *ebpftracer.Tracer, pid uint32) error {
 	p := c.processes[pid]
 	if p == nil {
-		return
+		return nil
 	}
 	if !p.openSslUprobesChecked {
-		p.uprobes = append(p.uprobes, tracer.AttachOpenSslUprobes(pid)...)
+		sslProbes, err := tracer.AttachOpenSslUprobes(pid)
+		if err != nil {
+			return err
+		}
+		p.uprobes = append(p.uprobes, sslProbes...)
 		p.openSslUprobesChecked = true
 	}
 	if !p.goTlsUprobesChecked {
 		codeType := c.GetCodeTypeFromCache(pid)
-		p.uprobes = append(p.uprobes, tracer.AttachGoTlsUprobes(pid, c.instanceID, uint16(codeType))...)
+		goProbes, err := tracer.AttachGoTlsUprobes(pid, &c.AppInfo, uint16(codeType))
+		if err != nil {
+			return err
+		}
+		p.uprobes = append(p.uprobes, goProbes...)
 		p.goTlsUprobesChecked = true
 	}
+	return nil
 }
 
-func (c *Container) attachJVMUprobes(tracer *ebpftracer.Tracer, pid uint32) {
-	ENV_PID := os.Getenv("FILTER_PID")
-	if ENV_PID != "" {
-		filterPid, _ := strconv.ParseInt(ENV_PID, 10, 64)
-		if filterPid != int64(pid) {
-			return
-		}
-	} else {
-		return
+func (c *Container) attachJVMUprobes(tracer *ebpftracer.Tracer, pid uint32) error {
+	if common.IsOpenFilter() && !common.IsFilterPid(pid) {
+		return nil
 	}
 	p := c.processes[pid]
 	if p == nil {
-		return
+		return nil
 	}
+	codeType := c.GetCodeTypeFromCache(pid)
 	if !p.jvmUprobesChecked {
-		codeType := c.GetCodeTypeFromCache(pid)
-		tracer.InitKProcInfo(pid, c.instanceID, uint16(codeType))
+		tracer.InitKProcInfo(pid, &c.AppInfo)
 
 		libNioProbes, err := tracer.AttachJavaNioReadUprobes(pid, codeType)
-		if err == nil {
-			p.uprobes = append(p.uprobes, libNioProbes...)
-		} else {
+		if err != nil {
 			klog.Error(err)
+			return err
 		}
+		p.uprobes = append(p.uprobes, libNioProbes...)
+
 		libNetProbes, err := tracer.AttachJavaNetWriteUprobes(pid)
-		if err == nil {
-			p.uprobes = append(p.uprobes, libNetProbes...)
-		} else {
+		if err != nil {
 			klog.Error(err)
+			return err
 		}
+		p.uprobes = append(p.uprobes, libNetProbes...)
 		p.jvmUprobesChecked = true
+	} else {
+		klog.Infof("[attach] %s-%d already attach", codeType.String(), pid)
 	}
+	return nil
 }
 
-func (c *Container) attachNetCoreUprobes(tracer *ebpftracer.Tracer, pid uint32) {
-	ENV_PID := os.Getenv("FILTER_PID")
-	if ENV_PID != "" {
-		filterPid, _ := strconv.ParseInt(ENV_PID, 10, 64)
-		if filterPid != int64(pid) {
-			return
-		}
+func (c *Container) errorClose(pid uint32) {
+	p := c.processes[pid]
+	if p != nil {
+		p.DynamicClose()
+	}
+}
+
+func (c *Container) attachNetCoreUprobes(tracer *ebpftracer.Tracer, pid uint32) error {
+	if common.IsOpenFilter() && !common.IsFilterPid(pid) {
+		return nil
 	}
 	p := c.processes[pid]
 	if p == nil {
-		return
+		return nil
 	}
 	if !p.jvmUprobesChecked {
-		codeType := c.GetCodeTypeFromCache(pid)
-		tracer.InitKProcInfo(pid, c.instanceID, uint16(codeType))
-		p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetThreadUprobes(pid, c.instanceID)...)
-		p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetReadUprobes(pid, c.instanceID)...)
-		p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetWriteUprobes(pid, c.instanceID)...)
-		// 以下 v2 版本
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetOnConnectionAsyncStartUprobes(pid, c.instanceID)...)
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetOnConnectionAsyncEndUprobes(pid, c.instanceID)...)
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetDoSendStartUprobes(pid, c.instanceID)...)
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetDoSendEndUprobes(pid, c.instanceID)...)
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetSendAsync1Uprobes(pid, c.instanceID)...)
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetSendAsync2Uprobes(pid, c.instanceID)...)
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetSendAsync2EndUprobes(pid, c.instanceID)...)
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetSocketConnectionStartStartUprobes(pid, c.instanceID)...)
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetSocketConnectionStartEndUprobes(pid, c.instanceID)...)
-		// 以下为 v1 版本
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetThreadUprobes(pid, c.instanceID)...)
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetReceiveStartUprobes(pid, c.instanceID)...)
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetReceiveEndUprobes(pid, c.instanceID)...)
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetSendStartUprobes(pid, c.instanceID)...)
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetSendEndUprobes(pid, c.instanceID)...)
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetDoReceiveEndUprobes(pid, c.instanceID)...)
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetDoReceiveStartUprobes(pid, c.instanceID)...)
-		// p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetCreateContextStartUprobes(pid, c.instanceID)...)
+		//codeType := c.GetCodeTypeFromCache(pid)
+		tracer.InitKProcInfo(pid, &c.AppInfo)
+		p.uprobes = append(p.uprobes, tracer.AttachNetCoreNetThreadUprobes(pid)...)
+		readProbes, err := tracer.AttachNetCoreNetReadUprobes(pid)
+		if err != nil {
+			klog.Error(err)
+			return err
+		}
+		p.uprobes = append(p.uprobes, readProbes...)
 
+		WriteProbes, err := tracer.AttachNetCoreNetWriteUprobes(pid)
+		if err != nil {
+			klog.Error(err)
+			return err
+		}
+		p.uprobes = append(p.uprobes, WriteProbes...)
 		p.jvmUprobesChecked = true
 	}
+
+	return nil
 }
 
 func resolveFd(pid uint32, fd uint64) (mntId string, logPath string) {

+ 181 - 85
containers/container_apm.go

@@ -11,16 +11,20 @@ import (
 	"strings"
 	"time"
 
-	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/ebpftracer"
 	"github.com/coroot/coroot-node-agent/ebpftracer/l7"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer"
+	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/coroot-node-agent/tracing"
 	"github.com/coroot/coroot-node-agent/utils"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
 	"github.com/pkg/errors"
+	klog "github.com/sirupsen/logrus"
 	"inet.af/netaddr"
 )
 
+const TRACE_STATUS = 1
+
 func (c *Container) getTrace(traceId uint64) (*tracing.Trace, bool) {
 	trace, ok := c.traceMap[traceId]
 	return trace, ok
@@ -68,7 +72,7 @@ func (c *Container) InitTrace(traceId uint64, r *l7.RequestData) error {
 func (c *Container) SendEvent(t *tracing.Trace, traceID uint64) {
 	if t.AllEventReady(traceID) {
 		t.SendEvent()
-		fmt.Printf("----send:%d \n", traceID)
+		klog.Infof("SendEvent %d", traceID)
 		//fmt.Println(t.GetSpan())
 		//fmt.Println("===============")
 		delete(c.traceMap, traceID)
@@ -83,39 +87,37 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 	c.lock.Lock()
 	defer c.lock.Unlock()
 	if r.Protocol == l7.ProtocolDNS {
-		fmt.Println("dns")
 		ip2fqdn, _type, fqdn := c.onDNSRequest(r)
-		apmTrace, err := c.getOrInitTrace(r.TraceId)
-		if err == nil {
-			apmTrace.DNSTraceQueryEvent(r, _type, fqdn)
-			c.SendEvent(apmTrace, r.TraceId)
+		if c.l7Attach && c.valuableTrace(r.TraceId) {
+			apmTrace, err := c.getOrInitTrace(r.TraceId)
+			if err == nil {
+				apmTrace.DNSTraceQueryEvent(r, _type, fqdn)
+				c.SendEvent(apmTrace, r.TraceId)
+			}
 		}
 		return ip2fqdn
 	}
 
-	if !c.valuableTrace(r.TraceId) {
-		return nil
-	}
-
-	if r.Protocol == l7.ProtocolTrace {
-		//fmt.Println("r.TraceStart:", r.TraceStart)
-		//fmt.Println("r.TraceEnd:", r.TraceEnd)
-
-		if r.TraceStart == 1 {
-			fmt.Println("====ProtocolTrace start====", r.TraceId)
+	//if !c.valuableTrace(r.TraceId) {
+	//	return nil
+	//}
 
+	if r.Protocol == l7.ProtocolTrace && c.l7Attach && c.valuableTrace(r.TraceId) {
+		if r.TraceStart == TRACE_STATUS {
+			klog.Infof("====ProtocolTrace start==== %d %d", pid, r.TraceId)
 			trace, err := c.getOrInitTrace(r.TraceId)
 			if err == nil {
 				method, path, hostIp, port := l7.ParseHttpHost(r.Payload)
 				ip, _ := netaddr.ParseIP(hostIp)
-				trace.TraceStartEvent(method, path, r.Status, netaddr.IPPortFrom(ip, port), c.GetCodeTypeFromCache(pid))
+				//codeType := c.GetCodeTypeFromCache(pid)
+				trace.TraceStartEvent(method, path, r.Status, netaddr.IPPortFrom(ip, port), pid, c.GetAppInfo())
 				c.SendEvent(trace, r.TraceId)
 			}
 
 			return nil
 		}
-		if r.TraceEnd == 1 {
-			fmt.Println("====ProtocolTrace end====", r.TraceId, r.EventCount)
+		if r.TraceEnd == TRACE_STATUS {
+			klog.Infof("====ProtocolTrace end==== %d %d", pid, r.TraceId)
 			trace, err := c.getOrInitTrace(r.TraceId)
 			if err == nil {
 				trace.TraceEndEvent(r)
@@ -125,22 +127,16 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 		}
 	}
 	if r.Protocol == l7.ProtocolHTTP {
-		//stats.observe(r.Status.Http(), "", r.Duration)
-		method, path, hostIp, port := l7.ParseHttpHost(r.Payload)
-		//trace.HttpRequest(method, path, r.Status, r.Duration)
-
-		//apmTrace, ok := c.getTrace(r.TraceId)
-		//if ok {
-		//	apmTrace.HttpTraceRequest(method, path, hostIp, port, r)
-		//}
-
-		apmTrace, err := c.getOrInitTrace(r.TraceId)
-		fmt.Println("ProtocolHTTP-----", r.TraceId, err)
-		if err == nil {
-			apmTrace.HttpTraceRequestEvent(method, path, hostIp, port, r)
-			c.SendEvent(apmTrace, r.TraceId)
+		if c.l7Attach && c.valuableTrace(r.TraceId) {
+			method, path, hostIp, port := l7.ParseHttpHost(r.Payload)
+			apmTrace, err := c.getOrInitTrace(r.TraceId)
+			//fmt.Println("ProtocolHTTP-----", r.TraceId, err)
+			if err == nil {
+				apmTrace.HttpTraceRequestEvent(method, path, hostIp, port, r)
+				c.SendEvent(apmTrace, r.TraceId)
+			}
 		}
-		return nil
+		//return nil
 	}
 	conn := c.connectionsByPidFd[PidFd{Pid: pid, Fd: fd}]
 	//fmt.Println("l7.connectionsByPidFd", conn, pid, fd)
@@ -152,19 +148,10 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 		return nil
 	}
 	stats := c.l7Stats.get(r.Protocol, conn.Dest, conn.ActualDest)
-	trace := tracing.NewTrace(string(c.id), conn.ActualDest)
+	//trace := tracing.NewTrace(string(c.id), conn.ActualDest)
 	switch r.Protocol {
 	case l7.ProtocolHTTP:
-		//fmt.Println("l7.ProtocolHTTP", r.TraceId)
-		////stats.observe(r.Status.Http(), "", r.Duration)
-		//method, path, hostIp, port := l7.ParseHttpHost(r.Payload)
-		////trace.HttpRequest(method, path, r.Status, r.Duration)
-		//
-		//apmTrace, ok := c.getTrace(r.TraceId)
-		//if ok {
-		//	apmTrace.HttpTraceRequest(method, path, hostIp, port, r)
-		//}
-
+		stats.observe(r.Status.Http(), "", r.Duration)
 	case l7.ProtocolHTTP2:
 		if conn.http2Parser == nil {
 			conn.http2Parser = l7.NewHttp2Parser()
@@ -172,39 +159,39 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 		requests := conn.http2Parser.Parse(r.Method, r.Payload, uint64(r.Duration))
 		for _, req := range requests {
 			stats.observe(req.Status.Http(), "", req.Duration)
-			trace.Http2Request(req.Method, req.Path, req.Scheme, req.Status, req.Duration)
+			//trace.Http2Request(req.Method, req.Path, req.Scheme, req.Status, req.Duration)
 		}
 	case l7.ProtocolPostgres:
-		//if r.Method != l7.MethodStatementClose {
-		//	stats.observe(r.Status.String(), "", r.Duration)
-		//}
+		if r.Method != l7.MethodStatementClose {
+			stats.observe(r.Status.String(), "", r.Duration)
+		}
 		//if conn.postgresParser == nil {
 		//	conn.postgresParser = l7.NewPostgresParser()
 		//}
 		//query := conn.postgresParser.Parse(r.Payload)
 		//trace.PostgresQuery(query, r.Status.Error(), r.Duration)
 	case l7.ProtocolMysql:
-		fmt.Println("l7.ProtocolMysql")
-		//fmt.Println(conn)
 		if r.Method != l7.MethodStatementClose {
 			stats.observe(r.Status.String(), "", r.Duration)
 		}
-		if conn.mysqlParser == nil {
-			conn.mysqlParser = l7.NewMysqlParser()
-		}
-		query := conn.mysqlParser.Parse(r.Payload, r.StatementId)
-		//trace.MysqlQuery(query, r.Status.Error(), r.Duration)
-
-		//apmTrace, ok := c.getTrace(r.TraceId)
-		apmTrace, err := c.getOrInitTrace(r.TraceId)
-		fmt.Println(err)
-		//fmt.Println("mysql r.TraceId:", r.TraceId)
-		//fmt.Println("ok:", ok)
-		//fmt.Println("traceMap:", len(c.traceMap))
-		if err == nil {
-			//apmTrace.MysqlTraceQuery(query, r.Status.Error(), r.Duration, conn.ActualDest)
-			apmTrace.MysqlTraceQueryEvent(query, r, conn.ActualDest)
-			c.SendEvent(apmTrace, r.TraceId)
+		if c.l7Attach && c.valuableTrace(r.TraceId) {
+			if conn.mysqlParser == nil {
+				conn.mysqlParser = l7.NewMysqlParser()
+			}
+			query := conn.mysqlParser.Parse(r.Payload, r.StatementId)
+			//trace.MysqlQuery(query, r.Status.Error(), r.Duration)
+
+			//apmTrace, ok := c.getTrace(r.TraceId)
+			apmTrace, err := c.getOrInitTrace(r.TraceId)
+			fmt.Println(err)
+			//fmt.Println("mysql r.TraceId:", r.TraceId)
+			//fmt.Println("ok:", ok)
+			//fmt.Println("traceMap:", len(c.traceMap))
+			if err == nil {
+				//apmTrace.MysqlTraceQuery(query, r.Status.Error(), r.Duration, conn.ActualDest)
+				apmTrace.MysqlTraceQueryEvent(query, r, conn.ActualDest)
+				c.SendEvent(apmTrace, r.TraceId)
+			}
 		}
 
 	case l7.ProtocolDM:
@@ -222,48 +209,78 @@ func (c *Container) onL7RequestApm(pid uint32, fd uint64, timestamp uint64, r *l
 		}
 		fmt.Println("---- onL7RequestApm ProtocolDM  end <----")
 	case l7.ProtocolMemcached:
-		//stats.observe(r.Status.String(), "", r.Duration)
+		stats.observe(r.Status.String(), "", r.Duration)
+		if c.l7Attach && c.valuableTrace(r.TraceId) {
+
+		}
+
 		//cmd, items := l7.ParseMemcached(r.Payload)
 		//trace.MemcachedQuery(cmd, items, r.Status.Error(), r.Duration)
 	case l7.ProtocolRedis:
 		stats.observe(r.Status.String(), "", r.Duration)
-		cmd, args := l7.ParseRedis(r.Payload)
-		fmt.Println("cmd", cmd)
-		fmt.Println("args", args)
-		//apmTrace, ok := c.getTrace(r.TraceId)
-		apmTrace, err := c.getOrInitTrace(r.TraceId)
-		if err == nil {
-			//apmTrace.RedisTraceQuery(cmd, args, r.Status.Error(), r.Duration)
-			apmTrace.RedisTraceQueryEvent(cmd, args, r, conn.ActualDest)
-			c.SendEvent(apmTrace, r.TraceId)
+		if c.l7Attach && c.valuableTrace(r.TraceId) {
+			cmd, args := l7.ParseRedis(r.Payload)
+			//fmt.Println("cmd", cmd)
+			//fmt.Println("args", args)
+			//apmTrace, ok := c.getTrace(r.TraceId)
+			apmTrace, err := c.getOrInitTrace(r.TraceId)
+			if err == nil {
+				//apmTrace.RedisTraceQuery(cmd, args, r.Status.Error(), r.Duration)
+				apmTrace.RedisTraceQueryEvent(cmd, args, r, conn.ActualDest)
+				c.SendEvent(apmTrace, r.TraceId)
+			}
 		}
 		//trace.RedisQuery(cmd, args, r.Status.Error(), r.Duration)
 	case l7.ProtocolMongo:
-		//stats.observe(r.Status.String(), "", r.Duration)
+		stats.observe(r.Status.String(), "", r.Duration)
+		if c.l7Attach && c.valuableTrace(r.TraceId) {
+		}
 		//query := l7.ParseMongo(r.Payload)
 		//trace.MongoQuery(query, r.Status.Error(), r.Duration)
 	case l7.ProtocolKafka, l7.ProtocolCassandra:
-		//stats.observe(r.Status.String(), "", r.Duration)
+		stats.observe(r.Status.String(), "", r.Duration)
+		if c.l7Attach && c.valuableTrace(r.TraceId) {
+		}
 	case l7.ProtocolRabbitmq, l7.ProtocolNats:
-		//stats.observe(r.Status.String(), r.Method.String(), 0)
+		stats.observe(r.Status.String(), r.Method.String(), 0)
+		if c.l7Attach && c.valuableTrace(r.TraceId) {
+		}
+	case l7.ProtocolDubbo2:
+		stats.observe(r.Status.String(), "", r.Duration)
+		if c.l7Attach && c.valuableTrace(r.TraceId) {
+		}
 	}
 	return nil
 }
 
-func (c *Container) buildInstanceID() {
+func (c *Container) buildIDs(pid uint32) bool {
 	c.lock.Lock()
 	defer c.lock.Unlock()
+	p := c.processes[pid]
+	if p != nil {
+		p.cmdline = string(proc.GetRealCmdline(pid))
+	}
 	for address, val := range c.getListens() {
 		if val == 1 {
 			ip := address.IP()
 			if ip.Is4() && !ip.IsLoopback() {
 				// 获取端口号
 				port := address.Port()
-				c.instanceID.IntVal, c.instanceID.HashtVal = utils.SetInsID(fmt.Sprintf("%s:%d", ip, port))
-				break
+				//c.instanceID.IntVal, c.instanceID.HashtVal, _ =
+				c.AppInfo.Sn = ip.String()
+				c.AppInfo.Sport = int(port)
+				strInstanceID := utils.BuildInt64ID(fmt.Sprintf("%s:%d", ip.String(), port))
+				c.AppInfo.InstanceIdHash.IntVal, _ = strInstanceID.ToInt64()
+				c.AppInfo.InstanceIdHash.HashtVal = strInstanceID.ToHashByte()
+				//c.AppInfo.InstanceId = c.instanceID.IntVal
+				strAgentID := utils.BuildInt64ID(fmt.Sprintf("%s:%s", strInstanceID, string(proc.GetExe(pid))))
+				c.AppInfo.AgentId, _ = strAgentID.ToInt64()
+				c.AppInfo.CodeType = c.GetCodeTypeFromCache(pid)
+				return true
 			}
 		}
 	}
+	return false
 }
 
 func (c *Container) StackProcess(event ebpftracer.StackEvent, tracer *ebpftracer.Tracer) {
@@ -309,7 +326,7 @@ func (c *Container) StackProcess2(event ebpftracer.StackEvent, tracer *ebpftrace
 	switch event.Location {
 	case 0: // ret
 		Funcname := ""
-		if event.Type != uint64(common.CodeTypeJava) {
+		if event.Type != uint64(CodeTypeJava) {
 			uprobe, err := c.GetUprobe(event, tracer)
 			if err != nil {
 				fmt.Println("GetUprobeGetUprobe errer: %v", err)
@@ -441,3 +458,82 @@ func (c *Container) GetUprobe(event ebpftracer.StackEvent, tracer *ebpftracer.Tr
 	err = errors.New("uprobe not found")
 	return
 }
+
+func (c *Container) GetAppInfo() AppInfo {
+	return c.AppInfo
+}
+
+// 可注入前置
+func (c *Container) checkEventReady() bool {
+	c.lock.Lock()
+	defer c.lock.Unlock()
+	return c.l7EventReady
+}
+
+func (c *Container) eventReady() {
+	c.lock.Lock()
+	defer c.lock.Unlock()
+	c.l7EventReady = true
+}
+
+// uprobe前置
+func (c *Container) checkL7AttachReady() bool {
+	c.lock.Lock()
+	defer c.lock.Unlock()
+	return c.l7Attach
+}
+
+func (c *Container) l7AttachSuccess() {
+	c.lock.Lock()
+	defer c.lock.Unlock()
+	c.l7Attach = true
+}
+
+func (c *Container) verifyAttachConditions(r *Registry, pid uint32) bool {
+	p := c.processes[pid]
+	if p != nil && c.checkEventReady() {
+		codeType := c.GetCodeTypeFromCache(pid)
+		if codeType.IsUnknownCode() {
+			klog.WithField("pid", pid).Infof("[verify] unknown language.")
+			return false
+		}
+		cmdline := p.GetCmdline()
+		if len(cmdline) == 0 {
+			return false
+		}
+		whiteListByCode := r.getWhiteListByCodeType(codeType)
+		klog.WithField("pid", pid).WithField("codeType", codeType.String()).
+			Infof("[verify] white list %v", whiteListByCode)
+		// 当前语言的白名单规则
+		for _, setting := range whiteListByCode {
+			ruleVal := setting.Filters
+			if ruleVal == "" {
+				continue
+			}
+			// 判断规则
+			if strings.Contains(cmdline, ruleVal) {
+				c.WhiteSettingInfo = setting
+				klog.WithField("pid", pid).
+					WithField("ruleVal", ruleVal).
+					WithField("cmdline", cmdline).
+					Infoln("[verify] check successful.")
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func (c *Container) detachUprobes(pid uint32) {
+	c.lock.Lock()
+	defer c.lock.Unlock()
+	// close uprobe
+	if p := c.processes[pid]; p != nil {
+		if len(p.uprobes) > 0 {
+			klog.Infof("detachUprobes", pid)
+			p.DynamicClose()
+			c.l7Attach = false
+			c.AppInfo = AppInfo{}
+		}
+	}
+}

+ 1 - 1
containers/containerd.go

@@ -13,7 +13,7 @@ import (
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/logparser"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 const containerdTimeout = 30 * time.Second

+ 1 - 1
containers/crio.go

@@ -13,7 +13,7 @@ import (
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/coroot/logparser"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 const crioTimeout = 30 * time.Second

+ 1 - 1
containers/dotnet.go

@@ -19,7 +19,7 @@ import (
 	"github.com/pyroscope-io/dotnetdiag"
 	"github.com/pyroscope-io/dotnetdiag/nettrace"
 	"github.com/pyroscope-io/dotnetdiag/nettrace/typecode"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 const (

+ 1 - 1
containers/jvm.go

@@ -10,8 +10,8 @@ import (
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/prometheus/client_golang/prometheus"
+	klog "github.com/sirupsen/logrus"
 	"github.com/xin053/hsperfdata"
-	"k8s.io/klog/v2"
 )
 
 func isJvm(cmdline []byte) bool {

+ 1 - 1
containers/l7.go

@@ -5,8 +5,8 @@ import (
 
 	"github.com/coroot/coroot-node-agent/ebpftracer/l7"
 	"github.com/prometheus/client_golang/prometheus"
+	klog "github.com/sirupsen/logrus"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 
 type L7Metrics struct {

+ 18 - 2
containers/process.go

@@ -2,7 +2,7 @@ package containers
 
 import (
 	"context"
-	"github.com/coroot/coroot-node-agent/common"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
 	"os"
 	"time"
 
@@ -29,7 +29,8 @@ type Process struct {
 	jvmUprobesChecked     bool
 	stackUprobesChecked   bool
 
-	codeType common.CodeType
+	codeType CodeType
+	cmdline  string
 }
 
 func NewProcess(pid uint32, stats *taskstats.Stats) *Process {
@@ -78,3 +79,18 @@ func (p *Process) Close() {
 		_ = u.Close()
 	}
 }
+
+func (p *Process) DynamicClose() {
+	for _, u := range p.uprobes {
+		_ = u.Close()
+	}
+	p.goTlsUprobesChecked = false
+	p.openSslUprobesChecked = false
+	p.jvmUprobesChecked = false
+	p.stackUprobesChecked = false
+	p.uprobes = []link.Link{}
+}
+
+func (p *Process) GetCmdline() string {
+	return p.cmdline
+}

+ 135 - 26
containers/registry.go

@@ -3,6 +3,12 @@ package containers
 import (
 	"bytes"
 	"fmt"
+	. "github.com/coroot/coroot-node-agent/utils"
+	"github.com/coroot/coroot-node-agent/utils/enums"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
+	"github.com/coroot/coroot-node-agent/utils/try"
+	. "github.com/coroot/coroot-node-agent/utils/worker"
+	log "github.com/sirupsen/logrus"
 	"os"
 	"regexp"
 	"strings"
@@ -16,9 +22,9 @@ import (
 	"github.com/coroot/coroot-node-agent/flags"
 	"github.com/coroot/coroot-node-agent/proc"
 	"github.com/prometheus/client_golang/prometheus"
+	klog "github.com/sirupsen/logrus"
 	"github.com/vishvananda/netns"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 
 var (
@@ -49,6 +55,10 @@ type Registry struct {
 	ip2fqdnLock          sync.Mutex
 
 	processInfoCh chan<- ProcessInfo
+
+	whiteListRules       WhiteListMap
+	whiteLastUpdatedTime int
+	connServer           ServerWorker
 }
 
 var (
@@ -111,11 +121,35 @@ func NewRegistry(reg prometheus.Registerer, kernelVersion string, processInfoCh
 
 		processInfoCh: processInfoCh,
 
-		tracer: ebpftracer.NewTracer(kernelVersion, *flags.DisableL7Tracing, *flags.DisableE2ETracing, *flags.DisableStackTracing),
+		tracer:         ebpftracer.NewTracer(kernelVersion, *flags.DisableL7Tracing, *flags.DisableE2ETracing, *flags.DisableStackTracing),
+		whiteListRules: make(WhiteListMap),
+	}
+	// 初始化软负载集群节点
+	proxyClient, clientErr := NewProxyClient(*flags.ConfigEndpoint, false)
+	if clientErr == nil {
+		// 负载健康检测
+		try.Go(proxyClient.CheckEndpoints, CatchFn)
+		log.Infof("New Proxy Client success.config_server is [%s]", "")
+	} else {
+		log.WithError(clientErr).Errorf("NewProxyClient error, Please check [export CONFIG_ENDPOINT=ip:port]")
+		return nil, clientErr
+	}
+
+	r.connServer, err = NewServerHTTPWorker()
+	if err != nil {
+		log.Errorf("init connServer error:%s.", err)
+		return nil, err
 	}
+
 	if err = reg.Register(r); err != nil {
 		return nil, err
 	}
+
+	//_, err = r.getWhiteList()
+	//if err != nil {
+	//	return nil, err
+	//}
+
 	go r.handleEvents(r.events)
 	if err = r.tracer.Run(r.events); err != nil {
 		close(r.events)
@@ -148,7 +182,61 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 	for {
 		select {
 		case now := <-gcTicker.C:
+			_, err := r.getWhiteList()
+			if err != nil {
+				log.WithError(err).Errorf("connWhiteList error")
+			}
+			runtimeApps := make(map[uint32]AppStatusInfo)
 			for pid, c := range r.containersByPid {
+				if !common.IsOpenFilter() {
+					verifyAttachConditions := c.verifyAttachConditions(r, pid)
+					if verifyAttachConditions {
+						err = c.RegisterAppInfo(r, pid)
+						if err == nil {
+							klog.WithField("pid", pid).Infoln("[registry] Attach uprobes.")
+							err = c.attachUprobes(r.tracer, pid)
+							if err != nil {
+								klog.WithField("pid", pid).WithError(err).Errorf("[registry] Failed attach uprobes error!")
+							} else {
+								klog.WithField("pid", pid).Infoln("[registry] Attach uprobes success!")
+							}
+							klog.WithField("pid", pid).Infoln("[registry] Attach app stack.")
+							err = c.stackTrace(r.tracer, pid)
+							if err != nil {
+								klog.WithField("pid", pid).WithError(err).Errorf("[registry][end] Failed attach stack trace!")
+							} else {
+								klog.WithField("pid", pid).Infoln("[registry] Attach Stack success!")
+							}
+						} else {
+							klog.WithError(err).Errorf("[registry] Failed registerAppInfo.")
+						}
+					}
+
+					if !verifyAttachConditions && c.checkL7AttachReady() {
+						// detach
+						c.detachUprobes(pid)
+					}
+				}
+				if c.AppInfo.AppName != "" {
+					detail := AppStatusInfo{
+						Pid:        pid,
+						ProcName:   c.containerName,
+						AppName:    c.AppInfo.AppName,
+						Language:   c.AppInfo.CodeType.String(),
+						AppID:      c.AppInfo.AppIdHash.IntVal,
+						AgentID:    c.AppInfo.AgentId,
+						InstanceID: c.AppInfo.InstanceIdHash.IntVal,
+						Sn:         c.AppInfo.Sn,
+						Sport:      c.AppInfo.Sport,
+						RegisterAt: time.Unix(c.AppInfo.RegisterAt, 0).Format("060102 15:04:05"),
+					}
+					if c.AppInfo.UpdateAt != 0 {
+						detail.UpdateAt = time.Unix(c.AppInfo.UpdateAt, 0).Format("060102 15:04:05")
+					}
+
+					runtimeApps[pid] = detail
+				}
+
 				cg, err := proc.ReadCgroup(pid)
 				if err != nil {
 					delete(r.containersByPid, pid)
@@ -162,6 +250,8 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 					c.onProcessExit(pid, false)
 				}
 			}
+			saveAppInfo(runtimeApps)
+
 			activeIPs := map[netaddr.IP]struct{}{}
 			for id, c := range r.containersById {
 				if !c.Dead(now) {
@@ -236,35 +326,54 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 				}
 
 			case ebpftracer.EventTypeListenOpen:
-				//fmt.Println("ebpftracer.EventTypeConnectionOpen==================", e.Pid)
+				//fmt.Println("ebpftracer.EventTypeListenOpen==================", e.Pid)
 				if c := r.getOrCreateContainer(e.Pid); c != nil {
 					c.onListenOpen(e.Pid, e.SrcAddr, false)
-					c.buildInstanceID()
-					//c.attachTlsUprobes(r.tracer, e.Pid)
-					// c.attachJVMUprobes(r.tracer, e.Pid)
-					c.attachUprobes(r.tracer, e.Pid)
-					err := c.StackTrace(r.tracer, e.Pid)
-					if err != nil {
-						klog.Errorf("Stack trace error", err)
+					// cmdline InstanceID agentID
+					if c.buildIDs(e.Pid) {
+						c.eventReady()
+					}
+					if common.IsOpenFilter() && common.IsFilterPid(e.Pid) {
+						c.WhiteSettingInfo.AppName = enums.TestApp
+						err := c.RegisterAppInfo(r, e.Pid)
+						if err != nil {
+							klog.WithError(err).Errorf("[registry] Failed registerAppInfo. pid is %d", e.Pid)
+							continue
+						}
+						c.attachUprobes(r.tracer, e.Pid)
+						err = c.StackTrace(r.tracer, e.Pid)
+						if err != nil {
+							klog.Errorf("Stack trace error", err)
+						}
 					}
 				} else {
 					klog.Infoln("TCP listen open from unknown container", e)
 				}
-			case ebpftracer.EventTypeListenClose:
-				if c := r.containersByPid[e.Pid]; c != nil {
-					c.onListenClose(e.Pid, e.SrcAddr)
-				}
-
 			case ebpftracer.EventTypeConnectionOpen:
 				//fmt.Println("ebpftracer.EventTypeConnectionOpen==================", e.Pid)
 				if c := r.getOrCreateContainer(e.Pid); c != nil {
 					c.onConnectionOpen(e.Pid, e.Fd, e.SrcAddr, e.DstAddr, e.Timestamp, false)
-					//c.attachTlsUprobes(r.tracer, e.Pid)
-					// c.attachJVMUprobes(r.tracer, e.Pid)
-					c.attachUprobes(r.tracer, e.Pid)
+					c.eventReady()
+					if common.IsOpenFilter() && common.IsFilterPid(e.Pid) {
+						c.WhiteSettingInfo.AppName = enums.TestApp
+						err := c.RegisterAppInfo(r, e.Pid)
+						if err != nil {
+							klog.WithError(err).Errorf("[registry] Failed registerAppInfo. pid is %d", e.Pid)
+							continue
+						}
+						c.attachUprobes(r.tracer, e.Pid)
+						err = c.stackTrace(r.tracer, e.Pid)
+						if err != nil {
+							klog.Errorf("Stack trace error", err)
+						}
+					}
 				} else {
 					klog.Infoln("TCP connection from unknown container", e)
 				}
+			case ebpftracer.EventTypeListenClose:
+				if c := r.containersByPid[e.Pid]; c != nil {
+					c.onListenClose(e.Pid, e.SrcAddr)
+				}
 			case ebpftracer.EventTypeConnectionError:
 				if c := r.getOrCreateContainer(e.Pid); c != nil {
 					c.onConnectionOpen(e.Pid, e.Fd, e.SrcAddr, e.DstAddr, 0, true)
@@ -286,13 +395,14 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 					}
 				}
 			case ebpftracer.EventTypeL7Request:
-				fmt.Println(" ========= EventTypeL7Request Begin===========")
-				fmt.Println(" ===========e.L7Request Payload:", string(e.L7Request.Payload))
+				//fmt.Println("e.L7Request Payload:", string(e.L7Request.Payload))
 				if e.L7Request == nil {
 					continue
 				}
 				if c := r.containersByPid[e.Pid]; c != nil {
-					fmt.Println(" ========= EventTypeL7Request Doing===========")
+					//fmt.Println("EventTypeL7Request", e.Pid, c.checkL7AttachReady())
+					//a, _ := json.Marshal(e.L7Request)
+					//fmt.Println("EventTypeL7Request", e.Pid, string(a))
 					ip2fqdn := c.onL7RequestApm(e.Pid, e.Fd, e.Timestamp, e.L7Request)
 					r.ip2fqdnLock.Lock()
 					for ip, fqdn := range ip2fqdn {
@@ -300,14 +410,13 @@ func (r *Registry) handleEvents(ch <-chan ebpftracer.Event) {
 					}
 					r.ip2fqdnLock.Unlock()
 				}
-				fmt.Println(" ========= EventTypeL7Request End===========")
 			case ebpftracer.EventTypeFunEnt:
 				if e.StackEvent == nil {
 					continue
 				}
 
 				if c := r.containersByPid[uint32(e.StackEvent.Pid)]; c != nil {
-					if e.StackEvent.Type == uint64(common.CodeTypeJava) {
+					if e.StackEvent.Type == uint64(CodeTypeJava) {
 						fmt.Printf("e.EventTypeFunEnt: TraceId:%d, Pid:%d, Location:%d, Goid:%d, TimeNs:%d, Ip:%X, CallerIp:%d, Bp:%d, CallerBp:%d\n", e.StackEvent.TraceId, e.StackEvent.Pid, e.StackEvent.Location, e.StackEvent.Goid, e.StackEvent.TimeNsStart, e.StackEvent.Ip, e.StackEvent.CallerIp, e.StackEvent.Bp, e.StackEvent.CallerBp)
 						fmt.Printf("e.EventTypeFunEnt: TraceId: MethedName: %d -- %s -- %s", e.StackEvent.Type, e.StackEvent.MethedName, e.StackEvent.ClassName)
 					} else {
@@ -363,9 +472,9 @@ func (r *Registry) getOrCreateContainer(pid uint32) *Container {
 	//klog.Infof("calculated container id %d -> %s -> %s", pid, cg.Id, id)
 	if id == "" {
 		if cg.Id == "/init.scope" && pid != 1 {
-			klog.InfoS("ignoring without persisting", "cg", cg.Id, "pid", pid)
+			klog.Infoln("ignoring without persisting", "cg", cg.Id, "pid", pid)
 		} else {
-			klog.InfoS("ignoring", "cg", cg.Id, "pid", pid)
+			klog.Infoln("ignoring", "cg", cg.Id, "pid", pid)
 			r.containersByPid[pid] = nil
 		}
 		return nil
@@ -393,7 +502,7 @@ func (r *Registry) getOrCreateContainer(pid uint32) *Container {
 		return nil
 	}
 
-	//klog.InfoS("detected a new container", "pid", pid, "cg", cg.Id, "id", id)
+	//klog.Infoln("detected a new container", "pid", pid, "cg", cg.Id, "id", id)
 	// add ns/workload/podname/pid/ctype
 	//sType := fmt.Sprintf("%d", cg.ContainerType)
 

+ 21 - 0
containers/registry_apm.go

@@ -1,8 +1,14 @@
 package containers
 
 import (
+	"encoding/json"
 	"fmt"
+	"github.com/coroot/coroot-node-agent/utils"
+	"github.com/coroot/coroot-node-agent/utils/modelse"
 	"github.com/prometheus/client_golang/prometheus"
+	"github.com/sirupsen/logrus"
+	"os"
+	"path"
 )
 
 const (
@@ -29,3 +35,18 @@ func setK8sTag(c *Container, tag map[string]string, pid uint32) {
 	c.K8sContainer.containerName = tag[ProcessName]
 	c.K8sContainer.pid = sPid
 }
+
+func saveAppInfo(runtimeApps map[uint32]modelse.AppStatusInfo) {
+	appStr, _ := json.Marshal(runtimeApps)
+	dumpPath := path.Join(utils.GetDefaultLogPath(), "memdump")
+	err := os.MkdirAll(dumpPath, 0755)
+	if err != nil {
+		logrus.Error(err)
+	}
+	fileName := fmt.Sprintf("%s.snap", "app")
+	metricsFileName := path.Join(dumpPath, fileName)
+	err = os.WriteFile(metricsFileName, appStr, 0644)
+	if err != nil {
+		logrus.Error(err)
+	}
+}

+ 26 - 29
containers/stack.go

@@ -6,19 +6,19 @@ import (
 	debugelf "debug/elf"
 	"fmt"
 	"github.com/coroot/coroot-node-agent/common"
-	"io"
-	"log"
-	"os"
-	"regexp"
-	"sort"
-	"strconv"
-
 	"github.com/coroot/coroot-node-agent/ebpftracer"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer"
 	tracerelf "github.com/coroot/coroot-node-agent/ebpftracer/tracer"
 	"github.com/coroot/coroot-node-agent/proc"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
+	klog "github.com/sirupsen/logrus"
 	"golang.org/x/arch/arm64/arm64asm"
 	"golang.org/x/arch/x86/x86asm"
+	"io"
+	"log"
+	"os"
+	"regexp"
+	"sort"
 )
 
 type uprobesDef struct {
@@ -33,22 +33,18 @@ func (c *Container) StackTrace(tracer *ebpftracer.Tracer, pid uint32) error {
 	if tracer.DisableStackTracing() {
 		return nil
 	}
-	// 进程过滤
-	ENV_PID := os.Getenv("FILTER_PID")
-	if ENV_PID != "" {
-		filterPid, _ := strconv.ParseInt(ENV_PID, 10, 64)
-		if filterPid != int64(pid) {
-			return nil
-		}
+
+	if common.IsOpenFilter() && !common.IsFilterPid(pid) {
+		return nil
 	}
-	// 类型过滤
+
 	codeType := c.GetCodeTypeFromCache(pid)
 	if codeType.IsUnknownCode() {
 		return nil
 	}
 
 	switch codeType {
-	case common.CodeTypeJava:
+	case CodeTypeJava:
 		return c.jvmStackTrace(tracer, pid)
 	default:
 		return c.stackTrace(tracer, pid)
@@ -84,7 +80,7 @@ func (c *Container) stackTrace(tracer *ebpftracer.Tracer, pid uint32) error {
 		binType = BIN_TYPE
 	}
 
-	fmt.Println("UprobesMatchString:::init", MatchString)
+	klog.Infoln("[stack] UprobesMatchString:::init", MatchString)
 	path := proc.Path(uint32(pid), "exe")
 
 	var err error
@@ -99,9 +95,9 @@ func (c *Container) stackTrace(tracer *ebpftracer.Tracer, pid uint32) error {
 	}
 
 	c.UprobesMap = map[string]tracerelf.Uprobe{}
-	fmt.Println("UprobesMap:::init")
+	klog.Infoln("[stack] UprobesMap start")
 	for _, up := range c.Uprobes {
-		fmt.Println("UprobesMap:::", up.Funcname, up.Address, up.AbsOffset)
+		klog.Debugf("[stack] UprobesMap %s %d %d", up.Funcname, up.Address, up.AbsOffset)
 		c.UprobesMap[fmt.Sprintf("%s-%s", up.Funcname, up.Address+up.AbsOffset)] = up
 	}
 	//codeType := c.GetCodeTypeFromCache(pid)
@@ -306,18 +302,18 @@ func (c *Container) getUprobes(path string, MatchString string) ([]tracer.Uprobe
 	// 符号表组装成键值 map,方便使用
 	symnames := map[string]debugelf.Symbol{}
 	for _, symbol := range symbols {
-		fmt.Println(symbol.Name, symbol)
+		klog.Debugf("[stack] %v %v", symbol.Name, symbol)
 		symnames[symbol.Name] = symbol
 	}
 
 	textSection := elfFile.Section(".text")
 	if textSection == nil {
-		fmt.Println("no text section", nil)
+		klog.Infoln("[stack] no text section")
 		return nil, nil
 	}
 	textSectionData, err := textSection.Data()
 	if err != nil {
-		fmt.Println("failed to read text section", err)
+		klog.WithError(err).Errorf("[stack] Failed to read text section")
 		return nil, nil
 	}
 	textSectionLen := uint64(len(textSectionData) - 1)
@@ -333,19 +329,20 @@ func (c *Container) getUprobes(path string, MatchString string) ([]tracer.Uprobe
 		// found, err := regexp.MatchString("main.*", symbol.Name)
 
 		if err != nil {
-			fmt.Println(err)
+			klog.WithError(err).Errorln("[stack] found error")
+			return nil, err
 		}
 
 		if found {
 			// 匹配到了加入 attachFuncs 列表
-			fmt.Printf("Fuck This: %s, %x\n", symbol.Name, symbol.Value)
+			klog.Debugf("[stack] Fuck This: %s, %x", symbol.Name, symbol.Value)
 			// attachFuncs = append(attachFuncs, symbol.Name)
 			// 根据函数名拿到当前函数的符号结构体
 			sym := symnames[symbol.Name]
-			if err != nil {
-				fmt.Printf("symnames[symbol.Name]", symbol.Name, err)
-				return nil, err
-			}
+			//if err != nil {
+			//	klog.WithError(err).Errorf("symnames[symbol.Name] %s", symbol.Name)
+			//	return nil, err
+			//}
 
 			address := sym.Value
 			for _, p := range elfFile.Progs {
@@ -397,7 +394,7 @@ func getRbpEnterOffsets(machine elf.Machine, instructions []byte) int {
 		for i := 0; i < len(instructions); {
 			ins, err := x86asm.Decode(instructions[i:], 64)
 			if err == nil && ins.Op == x86asm.LEA && ins.Args[0].String() == "RBP" {
-				fmt.Printf("getRbpEnterOffsets: %v, %s, %s\n", ins, ins.Args[0].String(), ins.Args[1].String())
+				klog.Infof("[stack] getRbpEnterOffsets: %v, %s, %s", ins, ins.Args[0].String(), ins.Args[1].String())
 				return i
 			}
 			i += ins.Len

+ 18 - 17
containers/util.go

@@ -3,6 +3,8 @@ package containers
 import (
 	"debug/elf"
 	"fmt"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
+	klog "github.com/sirupsen/logrus"
 	"io/ioutil"
 	"os"
 	"os/exec"
@@ -10,40 +12,39 @@ import (
 	"runtime"
 	"strings"
 
-	"github.com/coroot/coroot-node-agent/common"
 )
 
 var libjvmRegex = regexp.MustCompile(`.*/libjvm\.so`)
 
-func GetExeType(pid uint32) common.CodeType {
+func GetExeType(pid uint32) CodeType {
 	mapsFilePath := fmt.Sprintf("/proc/%d/maps", pid)
 
-	data, err := ioutil.ReadFile(mapsFilePath)
+	data, err := os.ReadFile(mapsFilePath)
 	if err != nil {
-		fmt.Println("Failed to read %s: %s", mapsFilePath, err)
-		return common.CodeTypeUnknown
+		klog.WithError(err).Errorf("Failed to read %s: %s", mapsFilePath)
+		return CodeTypeUnknown
 	}
 
 	content := string(data)
 
 	if libjvmRegex.MatchString(content) {
-		fmt.Println("is java process")
+		//fmt.Println("is java process")
 		if isJavaAotProcess(pid) {
-			fmt.Println("is javaAot process")
-			return common.CodeTypeJavaAot
+			//fmt.Println("is javaAot process")
+			return CodeTypeJavaAot
 		}
-		return common.CodeTypeJava
+		return CodeTypeJava
 	} else if isJavaAotProcess(pid) {
-		fmt.Println("is javaAot process")
-		return common.CodeTypeJavaAot
+		//fmt.Println("is javaAot process")
+		return CodeTypeJavaAot
 	} else if isGoProcess(pid) {
-		fmt.Println("is go process")
-		return common.CodeTypeGo
+		//fmt.Println("is go process")
+		return CodeTypeGo
 	} else if isNetCoreProcess(pid) {
-		fmt.Println("is netcore process")
-		return common.CodeTypeNetCoreAot
+		//	fmt.Println("is netcore process")
+		return CodeTypeNetCoreAot
 	}
-	return common.CodeTypeUnknown
+	return CodeTypeUnknown
 }
 
 // isJavaAotProcess checks if the process with the given PID is a GraalVM native image
@@ -106,7 +107,7 @@ func isGoProcess(pid uint32) bool {
 
 	gopclntabSection := ef.Section(".gopclntab")
 	if gopclntabSection != nil {
-		fmt.Println("is a go process")
+		//fmt.Println("is a go process")
 		return true
 	}
 	return false

+ 4 - 0
dist/.gitignore

@@ -0,0 +1,4 @@
+.idea
+installer.version
+Cloudwise-euspace-installer*.sh
+logs/*

+ 87 - 0
dist/README.md

@@ -0,0 +1,87 @@
+
+## package.sh 介绍
+
+[scripts/package.sh](../../scripts/package.sh) 主要是将 SmartAgent 安装目录下所有内容(脚本无自动去除无用目录或内容功能)压缩,
+通过`base64`编码后写入 [scripts/install_temp.sh](../../scripts/install_temp.sh) 中,
+最终生成安装文件 `cwserveragent-installer-version.sh` (version为SmartSgent版本)。
+
+### 安装帮助
+
+执行命令 `sh scripts/package.sh -h` 即可查看安装帮助详情。详情如下:
+
+```
+Usage: package.sh [-h] [-v] [DEBUG=true|false] [AGENT_VERSION=8.6]
+
+
+-h, --help     Display this help and exit.
+-v, --version  Print version and exit.
+
+RELEASE        Default true; Whether to delete the publish parameter in the script.
+VERSION        Configure the Cloudwise SmartAgent version.
+DEBUG          Default false; 1、Debug mode executed script package.sh;
+               2、Remove the debug log from the scripts (Cloudwise-SmartAgent-Linux-1.2.0.sh、smartagent、uninstall.sh).
+
+```
+
+### smartagent目录结构
+
+打包前目录结构(请保持目录及内容最简)
+```
+smartagent
+├── cwserveragent-installer-1.1.0.sh
+├── bin
+│   ├── CW-ServerAgent
+│   └── safe-rm
+├── conf
+│   └── server-agent.ini
+└── scripts
+    ├── install_temp.sh
+    ├── package.sh
+    ├── cw-serveragent
+    ├── uninstall.sh
+    ├── uninstall_temp.sh
+    └── xzdec
+```
+
+打包后目录结构
+```
+smartagent
+├── cwserveragent-installer-1.1.0.sh
+├── installer.version
+├── bin
+│   ├── CW-ServerAgent
+│   └── safe-rm
+├── conf
+│   └── server-agent.ini
+└── scripts
+    ├── install_temp.sh
+    ├── package.sh
+    ├── cw-serveragent
+    ├── uninstall.sh
+    ├── uninstall_temp.sh
+    └── xzdec
+```
+
+安装后目录结构
+
+```
+├── bin
+│   ├── CW-ServerAgent
+│   └── safe-rm
+├── conf
+│   ├── cwserveragent.conf
+│   ├── installation.conf
+│   └── server-agent.ini
+├── installer.version
+├── logs
+│   └── serveragent
+│       ├── CW-ServerAgent.error.log
+│       ├── CW-ServerAgent.fatal.log
+│       └── CW-ServerAgent.panic.log
+├── runtime
+│   └── .pid
+├── scripts
+│   ├── cw-serveragent
+│   └── uninstall.sh
+└── uninstall.sh
+```

+ 238 - 0
dist/package_dir/bin/agentctl

@@ -0,0 +1,238 @@
+#!/bin/bash
+
+#== 脚本执行中遇到不存在的变量就报错
+set -o nounset
+#== 脚本执行中报错即刻退出
+set -o errexit
+#== 脚本执行只要一个子命令失败,整个管道命令就失败
+set -o pipefail
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+readonly BRAND_PRODUCT_NAME="euspace"
+readonly BRAND_PARENT_PRODUCT_NAME="omniagent"
+readonly BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="euspace"
+#== **********************************************************
+#==  配置目录
+#== **********************************************************
+readonly INSTALL_BASE=/opt
+#== /opt/cloudwise/omniagent/agents 目录
+readonly BASE_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents
+#== cloudwise/omniagent/agents/agent
+readonly INSTALL_FOLDER=${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents/${BRAND_PRODUCT_NAME_LOWER}
+#== cloudwise/omniagent/agents/agent/version
+readonly INSTALL_VERSION_FOLDER=${INSTALL_FOLDER}/current
+#== /opt/cloudwise/omniagent/agents/agent/version
+readonly INSTALL_DIR=${INSTALL_BASE}/${INSTALL_VERSION_FOLDER}
+#== /opt/cloudwise/omniagent/agents/agent
+readonly AGENT_BASE_DIR=${INSTALL_BASE}/${INSTALL_FOLDER}
+
+readonly AGENT_BIN_DIR="${INSTALL_DIR}/bin"
+readonly AGENT_LOG_DIR="${INSTALL_DIR}/logs"
+
+#readonly AGENT_CONF_DIR="${INSTALL_DIR}/conf"
+#readonly MAIN_CONF_FILE="${AGENT_CONF_DIR}/${AGENT_MAIN_CONF}"
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+
+#== **********************************************************
+#==  配置重要文件名称
+#== **********************************************************
+readonly AGENT_PROC="euspace"
+readonly AGENT_CONFIG_PATH=""
+
+readonly AGENT_PID_FILE="${INSTALL_DIR}/bin/${AGENT_PROC}.pid"
+
+
+getDaemonPid(){
+    local agent="${1}"
+    #== 约定pid文件
+    local pidFilePath="${AGENT_PID_FILE}"
+    #== 二进制文件绝对路径
+    # shellcheck disable=SC2155
+    local procPath="$(readlink -m "${AGENT_BIN_DIR}/${agent}" 2>&1)"
+    local cmdline
+    local pid
+    #== 查询pid文件
+    if [ -s "${pidFilePath}" ] ; then
+      pid="$(cat "${pidFilePath}" 2>&1)"
+      cmdline="$(cat /proc/${pid}/cmdline 2>&1 | xargs -0 echo -n)"
+      if [ -d "/proc/${pid}" ] && ( [[ ${cmdline} =~ ${procPath} ]] || [[ ${cmdline} =~ ${AGENT_BIN_DIR}/${agent} ]] ); then
+#        toLogInfo "[${agent}] Find pid by ${pidFilePath} [content:${pid}][cmdline:${cmdline}]"
+          printf '%s' ${pid}
+          return 0
+      else
+        #== 进程绝对路径查询
+#        toLogWarning "[${agent}] Pidfile exist [content:${pid}]. But process not find or not myself. [cmdline:${cmdline}]"
+        if ! pid="$(listProcesses "${agent} pid" "${procPath}|${AGENT_BIN_DIR}/${agent}" "sudo|tail")"; then
+#          toLogWarning "[${agent}] Not find process grep '${procPath}'"
+          return 1
+        fi
+        cmdline="$(cat /proc/${pid}/cmdline 2>&1 | xargs -0 echo -n)"
+        if ! [[ ${cmdline} =~ ${procPath} || ${cmdline} =~ ${AGENT_BIN_DIR}/${agent} ]]; then
+#            toLogWarning "[${agent}] Find process success. But process not myself. [cmdline:${cmdline}]"
+            return 1
+        fi
+#        toLogInfo "[${agent}] Find pid by process name [${procPath}|${AGENT_BIN_DIR}/${agent}]"
+      fi
+    else
+      #== 进程绝对路径查询
+#      toLogWarning "[${agent}] Pidfile [${pidFilePath}] not find. Use abs path queries."
+      if ! pid="$(listProcesses "${agent} pid" "${procPath}|${AGENT_BIN_DIR}/${agent}" "sudo|tail")"; then
+#        toLogWarning "[${agent}] Not find process by grep '${procPath}'"
+        return 1
+      fi
+      cmdline="$(cat /proc/${pid}/cmdline 2>&1 | xargs -0 echo -n)"
+      if ! [[ ${cmdline} =~ ${procPath} || ${cmdline} =~ ${AGENT_BIN_DIR}/${agent} ]]; then
+#          toLogWarning "[${agent}] Find process success. But process not myself. [cmdline:${cmdline}]"
+          return 1
+      fi
+#      toLogInfo "[${agent}] Find pid by process name [${procPath}|${AGENT_BIN_DIR}/${agent}]."
+    fi
+
+    printf '%s' ${pid}
+    return 0
+}
+
+listProcesses() {
+  local errorMessage="${1}"
+  local includeRegex="${2}"
+
+  local excludeRegex=" grep"
+  if [ "${3}" ]; then
+    excludeRegex="grep|${3}"
+  fi
+
+#  toLogInfo "#DEBUG== listProcesses command: ps -e -o \"pid,args\" 2>&1 | grep -E "${includeRegex}" | awk '{{ print \$1,\$2 }}' | grep -vE \"${excludeRegex}\""
+  local output
+  if ! output="$(ps -e -o "pid,args" 2>&1 | grep -E "${includeRegex}" | awk '{{ print $1,$2 }}')"; then
+#    toLogError "Failed to get ${errorMessage}, output: ${output}"
+    return 1
+  fi
+
+  local foundProcesses="$(printf '%s' "${output}" | grep -vE "${excludeRegex}")"
+  if [ ! "${foundProcesses}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${foundProcesses}" | awk '{{ print $1 }}'
+  return 0
+}
+
+getAgentPid() {
+  local DAEMONPID="$(getDaemonPid "${AGENT_PROC}")"
+  if [ -z ${DAEMONPID} ]; then
+      return 1
+  fi
+  printf '%s' "${DAEMONPID}"
+
+}
+
+startAgent() {
+  local pid
+  if [ -f "${AGENT_BIN_DIR}/${AGENT_PROC}" ]; then
+    ${AGENT_BIN_DIR}/start.sh "${AGENT_BIN_DIR}/${AGENT_PROC}"
+#    local params="server --port 31767"
+#    if [ -f "/etc/chaosd/pki/ca.crt" ] && [ -f "/etc/chaosd/pki/chaosd.crt" ] && [ -f "/etc/chaosd/pki/chaosd.key" ]; then
+#        params="server  --https-port 31768 --CA=/etc/chaosd/pki/ca.crt --cert=/etc/chaosd/pki/chaosd.crt --key=/etc/chaosd/pki/chaosd.key"
+#    fi
+#    pid=$(nohup ${AGENT_BIN_DIR}/${AGENT_PROC} ${params}>>"${AGENT_LOG_DIR}/service.log" 2>&1 & echo $!)
+#    echo ${pid} > "${AGENT_PID_FILE}"
+  else
+    echo "${AGENT_BIN_DIR}/${AGENT_PROC} not exist."
+    exit "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+}
+
+agentStart() {
+  local agentPid
+  if agentPid="$(getAgentPid)"; then
+    echo "${BRAND_AGENT_PRODUCT_NAME} is running. Agent pid: ${agentPid}."
+    return
+  fi
+
+  startAgent
+	local exitCode=$?
+	if [ "${exitCode}" -ne 0 ]; then
+		echo "Failed to start ${BRAND_AGENT_PRODUCT_NAME} service."
+		exit "${EXIT_CODE_GENERIC_ERROR}"
+	fi
+
+  if agentPid="$(getAgentPid)"; then
+    echo "${BRAND_AGENT_PRODUCT_NAME} service started. Agent pid: ${agentPid}."
+  else
+    echo "${BRAND_AGENT_PRODUCT_NAME} not start!"
+    exit "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+agentStop() {
+  local agentPid
+  if agentPid="$(getAgentPid)"; then
+    ${AGENT_BIN_DIR}/stop.sh ${agentPid}
+#    kill ${agentPid}
+    echo "${BRAND_AGENT_PRODUCT_NAME} service stopped success."
+    cat /dev/null > "${AGENT_PID_FILE}"
+  else
+      echo "${BRAND_AGENT_PRODUCT_NAME} is stopped. service stopped success."
+  fi
+}
+
+agentRestart(){
+  agentStop
+  sleep 1
+  agentStart
+}
+
+agentStatus() {
+  local agentPid
+	local statusOutput
+  if agentPid="$(getAgentPid)"; then
+    statusOutput="${BRAND_AGENT_PRODUCT_NAME} is running. ${BRAND_AGENT_PRODUCT_NAME} pid: ${agentPid}"
+  else
+    statusOutput="${BRAND_AGENT_PRODUCT_NAME} is not running. ${BRAND_AGENT_PRODUCT_NAME} process not found or stop."
+  fi
+
+  echo "${statusOutput}"
+}
+
+info() {
+  local agentPid="$(getAgentPid)"
+  local version=$(cat "${INSTALL_DIR}/installer.version" | tr -d '\n')
+  printf '{"pid":%d,"version":"%s","agent_id":"%s","config":"%s"}' "${agentPid}" "${version}" "${BRAND_PRODUCT_NAME_LOWER}" "${AGENT_CONFIG_PATH}"
+}
+
+################################################################################
+#
+# Script start
+#
+################################################################################
+
+main() {
+  case "$1" in
+  start)
+    agentStart
+    ;;
+  stop)
+    agentStop
+    ;;
+  restart)
+    agentRestart
+    ;;
+  status)
+    agentStatus
+    ;;
+  info)
+    info
+    ;;
+  *)
+    toConsoleInfo "usage: $0 {start|stop|restart|status}"
+    ;;
+  esac
+
+  exit "${EXIT_CODE_OK}"
+}
+
+main "$@"

+ 6 - 0
dist/package_dir/bin/start.sh

@@ -0,0 +1,6 @@
+#!/bin/bash
+echo "start in start.sh"
+export TRACES_ENDPOINT=http://10.0.12.192:18080/docp/api/v2/data/receive
+pid=$(nohup $1 --listen="0.0.0.0:8123">>"/dev/null" 2>&1 & echo $!)
+AGENT_PID_FILE="$1.pid"
+echo ${pid} > "${AGENT_PID_FILE}"

+ 3 - 0
dist/package_dir/bin/stop.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+echo "stop in stop.sh"
+kill $1

+ 0 - 0
dist/package_dir/conf/.gitkeep


+ 2926 - 0
dist/scripts/install_temp.sh

@@ -0,0 +1,2926 @@
+#!/bin/bash
+#== AIX(Advanced Interactive eXecutive)是IBM基于AT&T Unix System V开发的一套类UNIX操作系统,运行在IBM专有的Power系列芯片设计的小型机硬件系统之上
+#== 以【#== 】开头的注释会在打包时删除
+#DEBUG== 以【#DEBUG== 】开头的注释会在调试打包时保留,正式打包时删除
+
+
+#==脚本执行中遇到不存在的变量就报错
+set -o nounset
+#==脚本执行中报错即刻退出
+set -o errexit
+#==脚本执行只要一个子命令失败,整个管道命令就失败
+set -o pipefail
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+readonly BRAND_PRODUCT_NAME="euspace"
+readonly BRAND_PARENT_PRODUCT_NAME="omniagent"
+readonly BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="euspace"
+readonly BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME_CLOUDWISE="cloudwise"
+
+readonly AIX_DF_SPECIFIC_FLAG=
+readonly CONF_LD_PRELOAD=
+
+PARAM_DISABLE_SYSTEM_LOGS_ACCESS=
+
+#== 安装包版本项
+readonly AGENT_BUILD_DATE=25.09.2020
+readonly AGENT_INSTALLER_VERSION=1.2.0
+readonly AGENT_BUILD_DATE_INFO=""
+readonly AGENT_BUILD_TAG=""
+readonly AGENT_BUILD_UPLOADER=""
+#== 安装包版本项
+
+#== **********************************************************
+#==  配置重要文件名称
+#== **********************************************************
+#== CW-ServerAgent
+readonly AGENT_PROC="euspace"
+#== **********************************************************
+#==  配置目录
+#== **********************************************************
+readonly INSTALL_BASE=/opt
+#== /opt/cloudwise/oneagent/agents 目录
+readonly BASE_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents
+#== cloudwise/oneagent/agents/chaosd
+readonly INSTALL_FOLDER=${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents/${BRAND_PRODUCT_NAME_LOWER}
+#== cloudwise/oneagent/agents/chaosd/version
+readonly INSTALL_VERSION_FOLDER=${INSTALL_FOLDER}/${AGENT_INSTALLER_VERSION}
+#== /opt/cloudwise/oneagent/agents/chaosd/version
+readonly INSTALL_DIR=${INSTALL_BASE}/${INSTALL_VERSION_FOLDER}
+#== /opt/cloudwise/oneagent/agents/chaosd
+readonly AGENT_BASE_DIR=${INSTALL_BASE}/${INSTALL_FOLDER}
+
+#== /opt/cloudwise/cwserveragent/conf
+readonly AGENT_CONF_DIR="${INSTALL_DIR}/conf"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/scripts
+readonly AGENT_SCRIPTS_DIR="${INSTALL_DIR}/scripts"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/runtime
+readonly AGENT_RUNTIME_DIR="${INSTALL_DIR}/runtime"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/bin
+readonly AGENT_BIN_DIR="${INSTALL_DIR}/bin"
+#== agent 初始化脚本目录 /opt/cloudwise/cwserveragent/lib
+#== readonly AGENT_LIB_DIR="${INSTALL_DIR}/lib"
+
+
+readonly AGENTS_BASE_DIR="${INSTALL_DIR}/agents"
+
+readonly META_BASE_DIR="${INSTALL_DIR}/meta"
+
+readonly LOG_DIR_NAME="logs"
+#== /opt/cloudwise/cwserveragent/logs
+readonly LOG_DIR=${INSTALL_DIR}/${LOG_DIR_NAME}
+
+#== /var/log/cloudwise/cwserveragent/
+readonly INSTALLER_LOG_DIR="/var/log/${INSTALL_FOLDER}"
+
+
+#== server-agent.ini
+#== hostId文件
+readonly INSTALLER_CONF_FILE_NAME="installation.conf"
+#== 部署在容器内的状态信息
+readonly INSTALLER_CONF_FILE="${AGENT_CONF_DIR}/${INSTALLER_CONF_FILE_NAME}"
+
+#== 【0】=
+readonly INSTALLER_LOCK_FILE="/tmp/${BRAND_FORMAL_NAME_LOWER}_${BRAND_PRODUCT_NAME_LOWER}.lock"
+
+readonly INIT_SYSTEM_SYSV="SysV"
+#== 系统 systemd
+readonly INIT_SYSTEM_SYSTEMD="systemd"
+#== cw-serveragent
+readonly SERVICE_SCRIPT_FILE="cw-oneagent"
+#== cw-serveragent.service
+readonly SYSTEMD_UNIT_FILE_AGENT="${SERVICE_SCRIPT_FILE}.service"
+#== 系统文件目录
+readonly SYSTEMD_UNIT_FILES_DIR="/etc/systemd/system/"
+
+#== 【0】=【退出码】执行成功
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+readonly EXIT_CODE_NOT_ENOUGH_SPACE=6
+readonly EXIT_CODE_NOT_ENOUGH_MEMORY=7
+#== 【0】=无效参数
+readonly EXIT_CODE_INVALID_PARAM=8
+readonly EXIT_CODE_INSUFFICIENT_PERMISSIONS=9
+
+#== 【退出码】接收信号
+readonly EXIT_CODE_SIGNAL_RECEIVED=12
+readonly EXIT_CODE_ANOTHER_INSTALLER_RUNNING=13
+readonly EXIT_CODE_CORRUPTED_PACKAGE=16
+#== 【退出码】管理系统环境变量配置
+readonly EXIT_CODE_MISCONFIGURED_ENVIRONMENT=17
+readonly EXIT_CODE_UNSUPPORTED_DOWNGRADE=18
+readonly EXIT_CODE_OS_NOT_SUPPORTED=19
+
+readonly EXTERNAL_TAR_SIZE=233820160
+readonly ARTIFACTS_SIZE=953271934
+#**********************************************************
+# Logs
+#**********************************************************
+toLogFile() {
+  if [ -e "${LOG_FILE}" ]; then
+    printf '%s UTC %s\n' "$(date -u +"%Y-%m-%d %H:%M:%S")" "$*" >>"${LOG_FILE}" 2>/dev/null
+  fi
+}
+
+toConsoleOnly() {
+  printf '%s %s\n' "$(date +"%H:%M:%S")" "$*" 2>/dev/null
+}
+
+toLogInfo() {
+  toLogFile "[INFO]" "$@"
+}
+
+toLogWarn() {
+  toLogFile "[WARN]" "$@"
+}
+
+toLogError() {
+  toLogFile "[ERROR]" "$@"
+}
+
+toLogAdaptive() {
+  local success="${1}"
+  shift
+  if [ "${success}" -eq 0 ]; then
+    toLogInfo "$@"
+  else
+    toLogError "$@"
+  fi
+}
+
+toConsoleInfo() {
+  toConsoleOnly "$@"
+  toLogInfo "$@"
+}
+
+toConsoleWarn() {
+  toConsoleOnly "Warn:" "$@"
+  toLogWarn "$@"
+} >&2
+
+toConsoleError() {
+  toConsoleOnly "Error:" "$@"
+  toLogError "$@"
+} >&2
+
+#== 【0】【3】【4】=创建不存在的目录,并设置权限
+createDirIfNotExistAndSetRights() {
+  local dir="${1}"
+  local rights="${2}"
+
+  toConsoleInfo "#DEBUG== createDirIfNotExistAndSetRights --- ${rights} --- ${dir}"
+  if [ ! -d "${dir}" ]; then
+    toLogInfo "Creating directory ${dir} with rights ${rights}"
+    local message
+    if ! message="$(mkdir -p "${dir}" 2>&1)"; then
+      toConsoleWarn "Cannot create ${dir} directory."
+      toLogWarn "Error message: ${message}"
+      return 1
+    fi
+  fi
+
+  if ! message="$(chmod "${rights}" "${dir}" 2>&1)"; then
+    toConsoleWarn "Cannot change permisions of ${dir} directory to ${rights}."
+    toLogWarn "Error message: ${message}"
+    return 1
+  fi
+
+  return 0
+}
+
+createCurrentVersionSymlink() {
+	toLogInfo "Creating symlink to current version..."
+
+	local currentVersionLink="${AGENT_BASE_DIR}/current"
+	local tempLink="${currentVersionLink}.tmp"
+
+	if ! commandErrorWrapper ln -s "${AGENT_INSTALLER_VERSION}" "${tempLink}"; then
+		toLogError "Failed to create current version link"
+		return
+	fi
+
+	if ! commandErrorWrapper arch_moveReplaceTarget "${tempLink}" "${currentVersionLink}"; then
+		toLogError "Failed to set up current version link"
+		commandErrorWrapper rm -f "${tempLink}"
+		return
+	fi
+
+	toLogInfo "Current version link created: ${currentVersionLink} -> ${AGENT_INSTALLER_VERSION}"
+}
+
+#== 【0】=创建日志目录及日志文件installation_$$.log
+createLogDirsIfMissing() {
+  createDirIfNotExistAndSetRights "${INSTALL_BASE}" u+rwx,g+rx,o+rx
+  createDirIfNotExistAndSetRights "${BASE_INSTALL_DIR}" u+rwx,g+rx,o+rx
+  #== agents根目录
+  createDirIfNotExistAndSetRights "${AGENT_BASE_DIR}" 1775
+  createDirIfNotExistAndSetRights "${INSTALL_DIR}" 1775
+#  createDirIfNotExistAndSetRights "${LOG_DIR}" 1777
+  createDirIfNotExistAndSetRights "${INSTALLER_LOG_DIR}" 1777
+
+#  for agentLog in ${PLUGIN_LOG_NAMES}; do
+#    createDirIfNotExistAndSetRights "${LOG_DIR}/${agentLog}" 1777
+#  done
+
+  touch "${LOG_FILE}"
+  setRightsForFiles "${LOG_FILE}" 766
+}
+
+#**********************************************************
+# Platform characteristics detection
+#**********************************************************
+
+#== 【5】=获取系统发型版本
+getOsReleasePath() {
+	local osReleasePath="/etc/os-release"
+	if [ ! -f "${osReleasePath}" ]; then
+		osReleasePath="/usr/lib/os-release"
+	fi
+
+	printf '%s' "${osReleasePath}"
+}
+
+parseOsReleaseFile() {
+	local osReleasePath="$(getOsReleasePath)"
+
+	#shellcheck disable=SC1090
+	. "${osReleasePath}"
+	local distrib="${NAME-}"
+	if [ -z "${distrib}" ]; then
+		distrib="${ID-}"
+	fi
+
+	local version="${VERSION_ID-}"
+	if printf '%s' "${distrib}" | grep -iq "debian"; then
+		version="$(cat /etc/debian_version)"
+	elif printf '%s' "${distrib}" | grep -iq "fedora" && [ "${VARIANT_ID-}" = "coreos" ]; then
+		distrib="${distrib} CoreOS"
+	fi
+
+	printf '%s %s' "${distrib}" "${version}"
+}
+
+#== 【5】=获取系统发型版本
+detectLinuxDistribution() {
+	if [ -f /etc/oracle-release ]; then
+		cat /etc/oracle-release
+	elif [ -f /etc/fedora-release ]; then
+		if [ -f "$(getOsReleasePath)" ]; then
+			(
+				parseOsReleaseFile
+			)
+		else
+			cat /etc/fedora-release
+		fi
+	elif [ -f /etc/redhat-release ]; then
+		cat /etc/redhat-release
+	elif [ -f "$(getOsReleasePath)" ]; then
+		(
+			parseOsReleaseFile
+		)
+	elif [ -f /etc/SuSE-release ]; then
+		head -1 /etc/SuSE-release
+	elif [ -f /etc/lsb-release ]; then
+		(
+			. /etc/lsb-release
+			printf "%s %s" "${DISTRIB_ID-}" "${DISTRIB_RELEASE-}"
+		)
+	elif ls /etc/*release* >/dev/null 2>&1; then
+		# Generic fallback
+		cat /etc/*release*
+	else
+		printf "AIX %s" "$(oslevel -s 2>&1)"
+	fi
+}
+
+#== 【3】=检查系统init (INIT_SYSTEM、INIT_SYSTEM_VERSION)
+checkInitSystem() {
+  local version
+  if version="$(systemctl --version 2>&1)"; then
+    if [ -d "${SYSTEMD_UNIT_FILES_DIR}" ]; then
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSTEMD}
+    else
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+      toLogWarn "${INIT_SYSTEM_SYSTEMD} was detected but ${SYSTEMD_UNIT_FILES_DIR} does not exist, using ${INIT_SYSTEM_SYSV} handling as a fallback"
+    fi
+  else
+    readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+    if ! version="$(init --version 2>&1)"; then
+      if ! version="$(chkconfig --version 2>&1)"; then
+        version="$(head -n1 /etc/inittab 2>&1)"
+      fi
+    fi
+  fi
+
+  readonly INIT_SYSTEM_VERSION="$(printf '%s' "${version}" 2>/dev/null | head -n1)"
+}
+
+#== 【3】=设置系统初始化脚本目录
+setLocationOfScripts() {
+  toLogInfo "Determining location of scripts..."
+
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSTEMD}"x ] || [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    #== /opt/cloudwise/cwserveragent/scripts
+    readonly INIT_DIR="${AGENT_SCRIPTS_DIR}"
+  else
+    if [ -d "/etc/init.d" ]; then
+      readonly INIT_DIR="/etc/init.d"
+    elif [ -d "/sbin/init.d" ]; then
+      readonly INIT_DIR="/sbin/init.d"
+    elif [ -d "/etc/rc.d" ]; then
+      readonly INIT_DIR="/etc/rc.d"
+    else
+      return 1
+    fi
+  fi
+
+  toLogInfo "Location of scripts ${INIT_DIR}"
+  return 0
+}
+
+#== 【0】=检查系统结构(X86_64\IA64\X86)
+detectArchitecture() {
+  local detected_arch=
+  if isAvailable arch; then
+    #== arch指令主要用于显示当前主机的硬件结构类型,查询结果与uname一致,我们可以看到它输出的结果有:i386、i486、mips、alpha等
+    detected_arch="$(arch | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  if [ -z "${detected_arch}" ]; then
+    detected_arch="$(uname -m | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${detected_arch}"
+}
+
+#**********************************************************
+# Misc functions
+#**********************************************************
+
+#== 【1】【4】【5】【6】=获取 agent (64\32)位数lib目录(""或lib64)
+getBinariesFolderByBitness() {
+  local bitness="${1}"
+  if [ "${bitness}" -eq 32 ]; then
+    bitness=""
+  fi
+  printf 'lib%s' "${bitness}"
+}
+
+#== 【1】【2】【5】【6】=获取 agent tools/lib64/smartagentctl 路径
+#== getAgentCtlBinPath
+
+#== 【1】【4】【5】【6】=获取 agent 64位数lib路径(lib64/installaction)
+#== getAgentInstallActionPath
+
+#== 【5】【6】=获取OS bin配置路径 /opt/cloudwise/cwserveragent/lib64/cloudwiseosconfig
+getOsConfigBinPath() {
+  printf "%s" "${INSTALL_DIR}/lib64/${AGENT_OS_CONFIG_BIN}"
+}
+
+#== 【5】=设置agent进程可用
+setProcessAgentEnabled() {
+  local enabled="${1}"
+  toLogInfo "Setting process agent enabled: ${enabled}..."
+  local changeStatus=
+  #== 调用 agent 64位数lib路径(lib64/installaction)执行指令 --set-process-agent-enabled
+  changeStatus=$("$(getAgentInstallActionPath)" --set-process-agent-enabled "${enabled}" 2>&1)
+  toLogAdaptive $? "Process agent enable(${enabled}) status: ${changeStatus}"
+}
+
+#== 【1】【4】【5】【6】=获取命令执行错误信息,并写日志
+commandErrorWrapper() {
+  local command="${*}"
+  local errorFile="/tmp/smartagent_commanderror_$$"
+
+  ${command} 2>"${errorFile}"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 0 ]; then
+    toLogWarn "Command '${command}' failed, return code: ${returnCode}, message: $(cat "${errorFile}")"
+  fi
+
+  rm -f "${errorFile}"
+
+  return ${returnCode}
+}
+
+#== 【3】=是否独立的namespace
+isNamespaceIsolated() {
+  local pid="${1}"
+  local namespace="${2}"
+  local initNamespaceId
+  local processNamespaceId
+  initNamespaceId="$(readlink "/proc/1/ns/${namespace}" 2>/dev/null | tr -dc '0-9')"
+  processNamespaceId="$(readlink "/proc/${pid}/ns/${namespace}" 2>/dev/null | tr -dc '0-9')"
+
+  if [ ! "${initNamespaceId}" ] || [ ! "${processNamespaceId}" ]; then
+    toLogInfo "Link to /proc/*/ns/${namespace} does not exist"
+    printf 'error'
+    return
+  fi
+
+  if [ "${initNamespaceId}"x != "${processNamespaceId}"x ]; then
+    printf 'true'
+  else
+    printf 'false'
+  fi
+}
+
+#== 【0】=检查是否root
+checkRootAccess() {
+  toConsoleInfo "Checking root privileges..."
+
+  if [ "$(id -u)"x != "0"x ]; then
+    toConsoleError "NOT OK"
+    return 1
+  fi
+
+  toConsoleInfo "OK"
+  return 0
+}
+
+#== 【3】=删除存在的路径
+removeIfExists() {
+  local pathToRemove="${1}"
+  if [ ! -e "${pathToRemove}" ]; then
+    toLogInfo "${pathToRemove} does not exist, skipping removal"
+    return
+  fi
+
+  local output
+  if ! output="$(rm -rf "${pathToRemove}" 2>&1)"; then
+    toLogWarn "Failed to remove ${pathToRemove}: ${output}"
+  fi
+}
+
+#**********************************************************
+# SELinux related functions
+#**********************************************************
+
+#== 【5】【6】=执行 systemctl 命令
+executeSystemctlCommand() {
+  local command="${1}"
+  local unit="${2}"
+
+  if [ "$(id -u)" != 0 ]; then
+    #== 执行使用 os config bin
+    executeUsingOsConfigBin "${command}" "${unit}"
+    return $?
+  fi
+
+  local output=
+  #== shellcheck disable=SC2086
+  #== 执行 systemctl 命令
+  output="$(systemctl "${command}" ${unit} 2>&1)"
+  local exitCode=$?
+
+  if [ ${exitCode} -eq 0 ]; then
+    toLogInfo "Successfully executed: systemctl ${command} ${unit}"
+  else
+    toLogError "Failed to execute: systemctl ${command} ${unit}"
+    toLogError "Command output: ${output}"
+    if [ -n "${unit}" ]; then
+      local reachBackNumSeconds=360
+      toLogError "journalctl output: $(journalctl -u "${unit}" --since=-${reachBackNumSeconds} 2>&1)"
+    fi
+  fi
+
+  return ${exitCode}
+} 2>>"${LOG_FILE}"
+
+#== 【6】=运行初始化命令(通过 service 方式 或 直接运行可执行命令)
+executeInitScriptCommand() {
+  local command=
+  local parameters="$*"
+  local output=
+  local exitCode=
+
+  if isAvailable service; then
+    command="service"
+    parameters="${SERVICE_SCRIPT_FILE} ${parameters}"
+  else
+    command="${INIT_DIR}/${SERVICE_SCRIPT_FILE}"
+  fi
+  output="$("${command}" "${parameters}" 2>&1)"
+  exitCode=$?
+  toLogAdaptive ${exitCode} "Executed ${command} ${parameters}, exitCode = ${exitCode}, output: ${output}"
+  return ${exitCode}
+}
+
+#== 【0】=信号捕获,并执行回调函数
+signalHandler() {
+  local signal="${1}"
+  local callback="${2}"
+  toLogWarn "process received signal: ${signal}"
+  ${callback}
+  exit ${EXIT_CODE_SIGNAL_RECEIVED}
+}
+
+#== 【0】=配置信号捕获和回调
+configureSignalHandling() {
+  local callback="${1}"
+  for signal in HUP INT QUIT ABRT ALRM TERM; do
+    # shellcheck disable=SC2064
+    trap "signalHandler '${signal}' '${callback}'" ${signal}
+  done
+
+  trap "" PIPE
+}
+
+#== 【0】删除权限
+removeSecretsFromString() {
+  printf "%s" "$*" | sed 's#\(LICENSE=\)[[:alnum:]]\{16\}#\1***#'
+}
+
+#== 【3】【5】=检查目录写权限
+testWriteAccessToDir() {
+  local errorFile="/tmp/smartagent_commandError_$$"
+  local dir="${1}"
+  local tmpfilename
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    tmpfilename="${dir}/.tmp_${BRAND_PRODUCT_NAME_LOWER}.$$${RANDOM}"
+    touch "${tmpfilename}" 2>"${errorFile}"
+  else
+    tmpfilename="$(mktemp -p "${dir}" ".tmp_${BRAND_PRODUCT_NAME_LOWER}.XXXXXXXXXXXXXX" 2>"${errorFile}")"
+  fi
+
+  #== shellcheck disable=SC2181
+  if [ $? -ne 0 ]; then
+    toLogInfo "$(cat "${errorFile}")"
+    rm -f "${errorFile}"
+    return 1
+  fi
+
+  rm -f "${tmpfilename}" "${errorFile}"
+  return 0
+}
+
+#== 【0】=设置PATH
+setPATH() {
+  local prependToPATH="/usr/sbin:/usr/bin:/sbin:/bin"
+  if [ "${PATH}" ]; then
+    PATH=${prependToPATH}:${PATH}
+  else
+    PATH=${prependToPATH}
+  fi
+}
+
+#== 【5】【6】=是否非root权限
+isNonRootModeEnabled() {
+  #== 调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-drop-root-privileges
+  local output
+  output="$(getValueFromConfigFile "${CONF_FIELD_NM_NON_ROOT_MODE}" "${LEGACY_AGENT_CONF_FILE}" "${PARAM_NON_ROOT_MODE}")"
+  printf '%s' "${output}" | grep -qE "(true|no_ambient)"
+}
+
+#== 【5】=自动启动工具
+runAutostartAddingTool() {
+  local prefix="${1}"
+  local file="${2}"
+  local suffix="${3}"
+  local output
+
+  local command="${prefix} ${file} ${suffix}"
+  toLogInfo "Executing ${command}"
+  output="$(${command} 2>&1)"
+  local status=$?
+  if [ "${output}" ]; then
+    toLogAdaptive ${status} "${output}"
+  fi
+
+  return ${status}
+}
+
+#== 【0】【5】=查找软链接真实地址(通过ls命令)
+readLinkFromLs() {
+  local path="${1}"
+  local result="${path}"
+  local lsOutput
+  local parsedLinkTarget
+  lsOutput="$(ls -dl "${path}" 2>/dev/null)"
+  if printf '%s' "${lsOutput}" | grep -q " -> "; then
+    parsedLinkTarget="$(printf '%s' "${lsOutput}" | sed 's|^.* -> ||')"
+    if [ "${parsedLinkTarget}" ]; then
+      result="${parsedLinkTarget}"
+    else
+      toLogWarn "Failed to parse ls output '${lsOutput}'"
+    fi
+  fi
+  printf '%s' "${result}"
+}
+
+#== 【0】【5】=查找软链接真实地址(通过readlink 或 ls 命令)
+readLink() {
+  local args=-e
+  local path="${1}"
+  if [ "${2}" ]; then
+    args="${1}"
+    path="${2}"
+  fi
+
+  (
+    if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+      path="${PATH}:/opt/freeware/bin"
+    fi
+
+    #== 通过 readlink 命令查找地址
+    if isAvailable readlink; then
+      #shellcheck disable=SC2086
+      readlink ${args} "${path}"
+    else
+      toLogInfo "readlink command not found, falling back to parsing ls output"
+      readLinkFromLs "${path}"
+    fi
+  )
+}
+
+#== 【0】【3】【4】【5】【6】=检查命令是否有效/是否存在
+isAvailable() {
+  command -v "${1}" >/dev/null 2>&1
+}
+
+#== 【0】【1】【3】【5】=读取配置文件配置参数信息
+getValueFromConfigFile() {
+  local key="${1}"
+  local configFile="${2}"
+  local defaultValue="${3}"
+  local value
+  value="$(sed -n "s|^${key}=||p" "${configFile}" 2>/dev/null)"
+
+  if [ "${value}" ]; then
+    printf '%s' "${value}"
+  else
+    printf '%s' "${defaultValue}"
+  fi
+}
+
+#== 【1】【5】=删除配置文件中配置
+removeValueFromConfigFile() {
+  local key="${1}"
+  local configFile="${2}"
+  local output
+  if ! output="$(cp -p "${configFile}" "${configFile}.tmp" 2>&1)"; then
+    toLogWarn "Unable to initialize ${configFile}.tmp file using source file, privileges and ownership will not be preserved: ${output}"
+  fi
+
+  if sed "/^${key}/d" "${configFile}" >"${configFile}.tmp"; then
+    mv -f "${configFile}.tmp" "${configFile}"
+  else
+    toLogWarn "Failed to remove ${key} from ${configFile}"
+    rm -f "${configFile}.tmp"
+  fi
+}
+
+#== 写配置到配置文件信息
+writeParamToConfigFile() {
+  local key="${1}"
+  local newValue="${2}"
+  local configFile="${3}"
+  local value
+  value="$(sed -n "s|^${key}=||p" "${configFile}" 2>/dev/null)"
+  toConsoleInfo "#DEBUG== writeParamToConfigFile --> edit ${key}=${value} to ${key}=${newValue}, configFile: ${configFile}"
+  if [ "${value}" ]; then
+    sed -i "s|^${key}=.*|${key}=${newValue}|" "${configFile}" 2>/dev/null
+  else
+    echo "${key}=${newValue}" >>"${configFile}" 2>/dev/null
+  fi
+}
+
+writeContentToConfigFile() {
+  local newValue="${1}"
+  local configFile="${2}"
+  toConsoleInfo "#DEBUG== writeContentToConfigFile --> edit ${newValue}, configFile: ${configFile}"
+
+  echo "${newValue}" > "${configFile}" 2>/dev/null
+}
+
+#== 修改scripts/smartagent脚本配置信息
+editScriptFileParam() {
+  local key="${1}"
+  local newValue="${2}"
+  local file="${3}"
+  local value
+  value="$(sed -n "s|^${key}=||p" "${file}" 2>/dev/null)"
+  toConsoleInfo "#DEBUG== editScriptFileParam --> edit ${key}=${value} to ${key}=${newValue}, configFile: ${file}"
+  if [ "${value}" ]; then
+    sed -i "s|^${key}=.*|${key}=${newValue}|" "${file}" 2>/dev/null
+  fi
+}
+
+#== 【0】=是否存在多个同时安装操作【通过判断 INSTALLER_LOCK_FILE 】
+isAnotherInstallationRunning() {
+  if [ ! -f "${INSTALLER_LOCK_FILE}" ]; then
+    return 1
+  fi
+  local pidFromLockFile
+  local nameFromLockFile
+  pidFromLockFile="$(head -n 1 "${INSTALLER_LOCK_FILE}")"
+  nameFromLockFile="$(tail -n 1 "${INSTALLER_LOCK_FILE}")"
+  if [ "$(wc -l <"${INSTALLER_LOCK_FILE}")" -ne 2 ] || [ -z "${pidFromLockFile}" ] || [ -z "${nameFromLockFile}" ]; then
+    toConsoleWarn "Installation lock file ${INSTALLER_LOCK_FILE} is damaged, skipping uniqueness check."
+    toConsoleWarn "Lock file contents: '$(cat ${INSTALLER_LOCK_FILE})'"
+    return 1
+  fi
+
+  #== shellcheck disable=SC2009
+  #== 获取正在执行安装的进程信息
+  local foundProcesses
+  foundProcesses="$(pgrep -f "pid,args" 2>&1 | grep -w "${nameFromLockFile}" | grep -v " grep ")"
+  if printf '%s' "${foundProcesses}" | awk '{ print $1 }' | grep -wq "${pidFromLockFile}"; then
+    local errorMessage="Another ${BRAND_PRODUCT_NAME} installer or uninstaller is already running"
+    if printf '%s' "${foundProcesses}" | grep -q "${DOWNLOADS_DIR}"; then
+      errorMessage="${errorMessage} (AutoUpdate is in progress)"
+    fi
+
+    toConsoleError "${errorMessage}, PID ${pidFromLockFile}. Exiting."
+    return 0
+  fi
+
+  toConsoleInfo "Lock file exists but corresponding installation process does not run, contents of lock file: ${pidFromLockFile}, ${nameFromLockFile}."
+  return 1
+}
+
+#== 【0】=创建安装标示文件 /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+createInstallationLockFile() {
+  printf '%s\n%s\n' "$$" "$0" >"${INSTALLER_LOCK_FILE}" 2>/dev/null
+}
+
+#== 【0】【6】删除安装标示文件 /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+removeInstallationLockFile() {
+  toLogInfo "Removing installation lock file."
+  rm -f "${INSTALLER_LOCK_FILE}"
+}
+
+#== 【0】基础启动日志信息
+logBasicStartupInformation() {
+  toLogInfo "Command line: $(removeSecretsFromString "${@}")"
+  toLogInfo "Shell options: $-"
+  toLogInfo "Working dir: $(pwd)"
+  toLogInfo "PID: $$"
+  toLogInfo "Parent process: $(
+    printf '\n'
+    ps -o user,pid,ppid,comm -p ${PPID} 2>&1
+  )"
+  toLogInfo "User id: $(id -u)"
+}
+
+#== 解压文件编码
+readonly UNPACK_BINARY=base64
+#== 解压文件编码参数
+readonly UNPACK_BINARY_ARGS="-di"
+
+readonly ARCH_ARCH="X86"
+
+#== 【0】=检查系统指令集(X86_64\IA64\X86)
+arch_checkArchitectureCompatibility() {
+  #== 【0】=检查系统结构(X86_64)
+  local arch
+  arch="$(detectArchitecture)"
+  if [ "${arch}"x = "X86_64"x ] || [ "${arch}"x = "IA64"x ]; then
+    arch="X86_64"
+  else
+    arch="$(uname -m | sed -e 's/i.86/x86/' | sed -e 's/i86pc/x86/' | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${arch}"
+  [ "${arch}"x = "X86_64"x ] || [ "${arch}"x = "AARCH64"x ]
+}
+
+#== 【5】=获取 lib 目录
+arch_getLibMacro() {
+  local libMacro=""
+  if [ "${SYSTEM_LIB32}" ]; then
+    #== shellcheck disable=SC2016
+    libMacro="/${LIB}"
+  fi
+  printf "%s" "${libMacro}"
+}
+#== 'timeout' requires gnu-coreutils8, i.e. it is not available on RHEL5, that's why we need this utility function
+#== 【3】【5】=执行命令超时配置
+
+#== 【0】【3】=获取文件权限信息
+arch_getAccessRights() {
+  stat --format='%A' "${1}"
+}
+
+#== 【4】=替换目录
+arch_moveReplaceTarget() {
+  local source="${1}"
+  local target="${2}"
+  mv -fT "${source}" "${target}"
+}
+
+
+#== xz 压缩包文件名 Cloudwise-SmartAgent
+readonly INTERNAL_TAR_FILE_NAME=${BRAND_FORMAL_NAME}-${BRAND_PRODUCT_NAME}.tar.xz
+
+#== -安装日志: installation-{timestamp}.log,如,installation-20220601110912.log
+readonly LOG_FILE="${INSTALLER_LOG_DIR}/installation-$(date -u +"%Y%m%d%H%M%S").log"
+#readonly LOG_FILE="${INSTALLER_LOG_DIR}/installation_$$.log"
+
+
+
+#== 【临时目录】安装临时目录
+readonly TMP_DIR=${INSTALL_DIR}_install_$$
+#== 【临时目录】解压缓存目录
+readonly UNPACK_CACHE=${BASE_INSTALL_DIR}/unpack_cache
+#== 【临时目录】从sh文件读取压缩文件 tarfile_$$.base64
+readonly EXTERNAL_TAR_FILE=${BASE_INSTALL_DIR}/tarfile_$$.base64
+#== 【临时目录】xz 压缩包文件
+readonly INTERNAL_TAR_FILE=${INSTALL_DIR}/${INTERNAL_TAR_FILE_NAME}
+
+#== 【0】=
+readonly INSTALLER_FILE=${0}
+#== 旧配置文件
+readonly LEGACY_AGENT_CONF_FILE="${AGENT_CONF_DIR}/${BRAND_PRODUCT_NAME_LOWER}.conf"
+
+#== 【0】=
+readonly LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS=50
+
+#== 【0】=
+readonly HELP_URL=""
+
+readonly CONF_FIELD_NM_DATA_SERVER="DataServer"
+readonly CONF_FIELD_NM_CONFIG_SERVER="ConfigServer"
+readonly CONF_FIELD_NM_LICENSE="License"
+readonly CONF_FIELD_NM_JSON_CONF="JSON_CONF"
+readonly CONF_FIELD_NM_USER="User"
+readonly CONF_FIELD_NM_GROUP="Group"
+readonly CONF_FIELD_NM_DATA_STORAGE="DataStorage"
+readonly CONF_FIELD_NM_NON_ROOT_MODE="NonRootMode"
+
+
+#== 配置默认用户权限
+readonly BASE_OMNI_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}
+readonly BASE_OMNI_INSTALL_CONF_DIR=${BASE_OMNI_INSTALL_DIR}/conf/installation.conf
+BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${BASE_OMNI_INSTALL_CONF_DIR}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME_CLOUDWISE}")"
+
+#== Those are read from params section appended to installer by the server
+#== 【0】=【参数】数据服务地址
+PARAM_DATA_SERVER=
+#== 【0】=【参数】配置服务地址
+PARAM_CONFIG_SERVER=
+#== 【0】=【参数】license
+PARAM_LICENSE=
+
+PARAM_JSON_CONF=
+
+#== 【0】=【参数】安装路径
+PARAM_INSTALL_DIR=${INSTALL_DIR}
+#== 【0】=【参数】用户
+PARAM_USER=
+#== 【0】=【参数】用户组
+PARAM_GROUP=
+#== 【0】=【参数】非root模式
+PARAM_NON_ROOT_MODE=true
+
+PARAM_USER_LOGIN=false
+
+#== 【0】=【参数】不允许root回退
+PARAM_DISABLE_ROOT_FALLBACK=false
+#== 【0】=【参数】数据存储目录
+PARAM_DATA_STORAGE=
+#== 【0】=【参数】通过容器部署
+#== PARAM_INTERNAL_DEPLOYED_VIA_CONTAINER=false
+#== 【0】=【参数】跳过SELinux策略安装
+#== PARAM_INTERNAL_SKIP_SELINUX_POLICY_INSTALLER=false
+#== 【0】=【参数】是否使用解压缓存
+PARAM_INTERNAL_USE_UNPACK_CACHE=false
+#== 【0】=【参数】是否不使用 dump
+#== PARAM_INTERNAL_DISABLE_DUMPPROC=
+#== 【0】=【参数】是否跳过非root检查
+PARAM_INTERNAL_NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK=false
+#== 【0】【1】=【参数】额外的配置
+#== PARAM_INTERNAL_PASS_THROUGH_SETTERS=
+#== 【0】=检查是否降级安装
+SKIP_DOWNGRADE_CHECK=false
+
+SKIP_PRIVILEGES_CHECK=false
+
+#== 自定义字符串参数
+PARAM_TEST=
+#== 自定义字BOOL参数
+PARAM_BOOL=
+
+#== 【0】=常规日志
+initializeLog() {
+  toConsoleInfo "Installation started, version ${AGENT_INSTALLER_VERSION}, build date: ${AGENT_BUILD_DATE}, PID $$."
+  toLogInfo "Started from: ${INSTALLER_FILE}"
+
+  if [ -f /proc/version ]; then
+    toLogInfo "System version: $(cat /proc/version)"
+  else
+    toLogInfo "System version: $(uname -a)"
+  fi
+
+  toLogInfo "Path: ${PATH}"
+  toLogInfo "INSTALL_DIR: ${INSTALL_DIR}"
+  toLogInfo "Resolved installation path: $(readLink -e "${INSTALL_DIR}" 2>/dev/null)"
+  logBasicStartupInformation "${@}"
+}
+
+#**********************************************************
+# Signing related stuff
+#**********************************************************
+#== 【0】【4】=通过占位分割读取位置
+locateDelimiter() {
+  #== 占位符
+  local delimiter="${1}"
+  #== 从文件结尾读取行数
+  local linesToReadFromEnd="${2}"
+  local linesCount
+  local offset
+  if [ "${linesToReadFromEnd}" ]; then
+    #== 文件总行数n(实际行数=n+1)
+    linesCount="$(wc -l "${INSTALLER_FILE}" | awk '{print $1}')"
+    #== 从后往前读取【linesToReadFromEnd】行
+    offset="$(tail -n"${linesToReadFromEnd}" "${INSTALLER_FILE}" 2>/dev/null | awk '/^'"${delimiter}"'/ { print NR; exit }')"
+    if [ -n "${offset}" ]; then
+      printf "%d" "$((linesCount - linesToReadFromEnd + offset))"
+    fi
+  else
+    #== 读取占位符所在行(实际行数=n+1)
+    awk '/^'"${delimiter}"'/ { print NR; exit }' "${INSTALLER_FILE}"
+  fi
+}
+
+#== 【0】=从指定行范围读取配置参数
+readParam() {
+  local paramName="${1}"
+  local paramsSectionBeggining="${2}"
+  local paramsSectionEnd="${3}"
+
+  sed -n "${paramsSectionBeggining},${paramsSectionEnd} s/^${paramName}=//p" "${INSTALLER_FILE}"
+}
+
+#== 【0】=读取以【----PARAMETERS】开始到 【----PARAMETERS--】之间的参数
+readParamsSection() {
+  local sectionName="----PARAMETERS"
+  local begin
+  local end
+  begin=$(locateDelimiter "${sectionName}" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS})
+  end=$(locateDelimiter "${sectionName}--" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS})
+
+  if [ -z "${begin}" ] || [ -z "${end}" ]; then
+    return
+  fi
+  #== 从指定行范围读取配置参数
+  local value
+  if value="$(readParam PARAM_DATA_SERVER "${begin}" "${end}")"; then
+    PARAM_DATA_SERVER="${value}"
+  fi
+  if value="$(readParam PARAM_CONFIG_SERVER "${begin}" "${end}")"; then
+    PARAM_CONFIG_SERVER="${value}"
+  fi
+  if value="$(readParam PARAM_LICENSE "${begin}" "${end}")"; then
+    PARAM_LICENSE="${value}"
+  fi
+
+  if value="$(readParam PARAM_JSON_CONF "${begin}" "${end}")"; then
+    PARAM_JSON_CONF="${value}"
+  fi
+
+  if value="$(readParam PARAM_USER "${begin}" "${end}")"; then
+    PARAM_USER="${value}"
+  fi
+  if value="$(readParam PARAM_GROUP "${begin}" "${end}")"; then
+    PARAM_GROUP="${value}"
+  fi
+  if value="$(readParam PARAM_NON_ROOT_MODE "${begin}" "${end}")"; then
+    if value="$(getBoolParam "${value}")"; then
+      PARAM_NON_ROOT_MODE="${value}"
+    fi
+  fi
+
+  if value="$(readParam PARAM_USER_LOGIN "${begin}" "${end}")"; then
+    if value="$(getBoolParam "${value}")"; then
+      PARAM_USER_LOGIN="${value}"
+    fi
+  fi
+  toConsoleInfo "#DEBUG== install sh params, DATA_SERVER: ${PARAM_DATA_SERVER}, PARAM_CONFIG_SERVER: ${PARAM_CONFIG_SERVER}, PARAM_LICENSE:${PARAM_LICENSE}"
+  toConsoleInfo "#DEBUG== install sh params, PARAM_USER: ${PARAM_USER}, PARAM_GROUP: ${PARAM_GROUP}, PARAM_USER_LOGIN: ${PARAM_USER_LOGIN}, PARAM_NON_ROOT_MODE:${PARAM_NON_ROOT_MODE}"
+}
+
+#== 【0】【6】=清空安装临时文件
+#== ${INSTALL_DIR}_install_$$
+#== EXTERNAL_TAR_FILE=${INSTALL_DIR}/tarfile_$$.base64
+#== ${INSTALL_DIR}/Dynatrace-OneAgent.tar.xz
+#== ${INSTALL_DIR}/xzdec
+#== /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+cleanInstallationTemporaryFiles() {
+  toLogInfo "Cleaning installation temporary files"
+
+  rm -f "${EXTERNAL_TAR_FILE}" "${INTERNAL_TAR_FILE}" "${INSTALL_DIR}/xzdec"
+  rm -Rf "${TMP_DIR}"
+
+  local keepInstallationLockFile="${1}"
+  if [ -z "${keepInstallationLockFile}" ]; then
+    removeInstallationLockFile
+  fi
+}
+
+#== 完成安装后清理临时目录
+finishInstallation() {
+  if [ $# -eq 2 ]; then
+    cleanInstallationTemporaryFiles "${2}"
+  else
+    cleanInstallationTemporaryFiles ""
+  fi
+  toLogInfo "Installation finished, PID $$, exit code: ${1}."
+  changeWorkingDir "${CURR_PATH}"
+  if [ "${CONF_LD_PRELOAD}"x = "true"x ]; then
+    exec /bin/bash && exit 0
+  fi
+  exit "${1}"
+}
+
+#**********************************************************
+# Create folders, copy files, set rights
+#**********************************************************
+
+#== 【3】=创建临时目录
+prepareTempFolder() {
+  #== 删除存在的路径
+  removeIfExists "${TMP_DIR}"
+
+  toLogInfo "Creating temporary folder $TMP_DIR"
+  createDirIfNotExistAndSetRights "${TMP_DIR}" 755
+}
+
+#== 【4】=设置文件可执行权限
+setRightsForFiles() {
+  local file="${1}"
+  local perms="${2}"
+
+  if [ -e "${file}" ]; then
+    chmod "${perms}" "${file}"
+  fi
+}
+
+setRightsForDir() {
+  local dir="${1}"
+  local perms="${2}"
+
+  if [ -d "${dir}" ]; then
+    chmod -R "${perms}" "${dir}"
+  fi
+}
+
+#== 【4】=安装类型(f:文件,d:目录)递归设置权限
+chmod4FilesRecursively() {
+  local dir="${1}"
+  local type="${2}"
+  local mask="${3}"
+  find "${dir}" -type "${type}" -exec chmod "${mask}" {} \;
+}
+
+#== 【4】=移动目录到指定位置
+moveFolderToDestination() {
+  local source="${1}"
+  local destination="${2}"
+  local fullDestination
+  fullDestination="${destination}/$(basename "${source}")"
+  local output
+
+  toLogInfo "Moving ${source} to ${destination}"
+  if [ ! -e "${fullDestination}" ]; then
+    if output="$(mv -f "${source}" "${destination}" 2>&1)"; then
+      toLogInfo "Moving Successfully."
+      return
+    fi
+    toLogWarn "Failed to move ${source} to ${destination}: ${output}, attempting to copy"
+  else
+    toLogInfo "${fullDestination} already exists, attempting to copy"
+  fi
+
+  if ! output="$(cp -Rfp "${source}" "${destination}" 2>&1)"; then
+    toLogError "Failed to copy ${source} to ${destination}: ${output}"
+  fi
+}
+
+#== 【4】=将 bin 下版本内容移动到安装目录
+installVersionedContent() {
+  toLogInfo "Installing versioned content..."
+  createDirIfNotExistAndSetRights "${AGENT_BIN_DIR}" 755
+
+  local sourceDir="${TMP_DIR}/bin"
+  if [ ! -d "${AGENT_BIN_DIR}" ]; then
+    moveFolderToDestination "${sourceDir}" "${AGENT_BIN_DIR}"
+    return
+  fi
+
+  toLogInfo "Directory ${AGENT_BIN_DIR} already exist, repairing the directory"
+  rm -rf "${AGENT_BIN_DIR}"
+  moveFolderToDestination "${sourceDir}" "${AGENT_BIN_DIR}"
+}
+
+#== 【4】=创建当前版本软连接
+
+#== 【4】=删除存在的目录
+listAndRemoveDirectoryIfExists() {
+  local directory="${1}"
+  if [ -d "${directory}" ]; then
+    toLogInfo "${directory} exists, removing it."
+    toLogInfo "Contents: $(ls -lR "${directory}")"
+    rm -rf "${directory}"
+  fi
+}
+
+#== 【4】=临时目录中conf 移动到 agent 安装目录中
+setupConfFolder() {
+  toLogInfo "Setup conf folder..."
+  #== 移动目录到指定位置
+  moveFolderToDestination "${TMP_DIR}/conf" "${INSTALL_DIR}"
+
+  chmod 755 "${AGENT_CONF_DIR}"
+
+  toLogInfo "Setup conf done."
+}
+
+#== 【4】=给距离当前目录至少 ${mindepth} 个子目录的所有文件设置权限
+chmodFilesWithMindepth() {
+  local dir="${1}"
+  local mindepth="${2}"
+  local mask="${3}"
+
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    #== 安装类型(f:文件,d:目录)递归设置权限
+    chmod4FilesRecursively "${1}" f "${3}"
+  else
+    #== 查找深度距离当前目录至少 ${mindepth} 个子目录的所有文件
+    find "${dir}" -mindepth "${mindepth}" -print0 | xargs -r -0 chmod "${mask}"
+  fi
+}
+
+#== 【4】=配置其他文件(cwserveragent.service、installer.version)
+setupMiscFiles() {
+  toLogInfo "Setup misc files..."
+  #== 创建 cwserveragent.service
+  createSystemdUnitFile
+  #== echo "${AGENT_INSTALLER_VERSION}" >"${INSTALL_DIR}/installer.version"
+  mv -f "${TMP_DIR}/installer.version" "${INSTALL_DIR}/"
+  toLogInfo "Setup misc done."
+}
+
+#== 【4】=创建 cwserveragent.service
+createSystemdUnitFile() {
+  toLogInfo "creating init scripts ${AGENT_SCRIPTS_DIR}"
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+    return
+  fi
+  createDirIfNotExistAndSetRights "${AGENT_SCRIPTS_DIR}" 755
+  cat <<EOF >${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}
+[Unit]
+Description=${BRAND_AGENT_PRODUCT_NAME}
+After=network-online.target
+Wants=network-online.target
+
+[Service]
+User=root
+ExecStart=${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE} start
+ExecStop=${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE} stop
+Type=forking
+#Restart=always
+KillMode=process
+TimeoutSec=240
+
+[Install]
+WantedBy=multi-user.target
+EOF
+
+  setRightsForFiles "${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" 755
+  if [ "${INIT_DIR}"x != "${AGENT_SCRIPTS_DIR}"x ]; then
+    cp -f "${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" ${INIT_DIR}
+    toLogInfo "Copy scripts ${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT} to ${INIT_DIR} done."
+  fi
+  toLogInfo "creating init scripts ${AGENT_SCRIPTS_DIR} done."
+}
+
+setupAll() {
+#  moveFolderToDestination "${TMP_DIR}/package_dir/*" "${INSTALL_DIR}"
+#ls  ${TMP_DIR}/package_dir/*
+
+  #== 移动所有文件到目录
+  cp -Rfp ${TMP_DIR}/package_dir/* "${INSTALL_DIR}"
+  local installVersionFile="${INSTALL_DIR}/installer.version"
+  echo ${AGENT_INSTALLER_VERSION} > ${installVersionFile}
+#  mv ${INSTALL_DIR}/package_dir/* ${INSTALL_DIR}
+#     echo "${TMP_DIR}/*" "${INSTALL_DIR}/"
+}
+
+#== 【4】=配置 lib、conf、bin、cwserveragent.service、installer.version、plugins
+setupOptDir() {
+  createDirIfNotExistAndSetRights "${INSTALL_DIR}" 1775
+  #== 临时目录中conf 移动到 agent 安装目录中
+  setupConfFolder
+  #== 将 bin 下版本内容移动到安装目录
+  installVersionedContent
+  #== 配置其他文件(cwserveragent.service、installer.version)
+  setupMiscFiles
+}
+
+#== 【4】=复制临时目录 scripts/cwserveragent 到指定目录下
+copyScriptsToDirectory() {
+  local scriptLocation="${1}"
+  #== scripts/cwserveragent
+  local scriptFile="${TMP_DIR}/scripts/${SERVICE_SCRIPT_FILE}"
+
+  toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} begin."
+
+  local output
+  if ! output="$(cp "${scriptFile}" "${scriptLocation}/" 2>&1)"; then
+    toLogError "Failed to copy ${scriptFile} to ${scriptLocation}, output: ${output}"
+    return
+  fi
+
+  setRightsForFiles "${scriptLocation}/${SERVICE_SCRIPT_FILE}" 755
+  toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} done."
+
+  #== scripts/uninstall.sh
+#  local scriptLocation="${1}"
+#    #== scripts/cwserveragent
+#    local scriptFile="${TMP_DIR}/scripts/uninstall.sh"
+#
+#    toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} begin."
+#
+#    local output
+#    if ! output="$(cp "${scriptFile}" "${scriptLocation}/" 2>&1)"; then
+#      toLogError "Failed to copy ${scriptFile} to ${scriptLocation}, output: ${output}"
+#      return
+#    fi
+#    setRightsForFiles "${scriptLocation}/uninstall.sh" 755
+#    toLogInfo "Copy scripts ${scriptFile} to ${scriptLocation} done."
+}
+
+#== 【4】=复制临时目录 scripts/cwserveragent 到指定目录下
+copyScripts() {
+  toLogInfo "Copy scripts..."
+  #== 创建目录 /opt/cloudwise/cwserveragent/scripts
+  createDirIfNotExistAndSetRights "${AGENT_SCRIPTS_DIR}" 755
+  #== 创建目录 /opt/cloudwise/{product}/agents
+  createDirIfNotExistAndSetRights "${AGENTS_BASE_DIR}" 775
+  #== 创建目录 /opt/cloudwise/{product}/meta
+  createDirIfNotExistAndSetRights "${META_BASE_DIR}" 775
+  #== 创建目录 /opt/cloudwise/{product}/runtime
+  createDirIfNotExistAndSetRights "${AGENT_RUNTIME_DIR}" 775
+
+  #== scripts/uninstall.sh
+  local uninstallScript="${TMP_DIR}/scripts/uninstall.sh"
+  toLogInfo "Copy scripts ${uninstallScript} to ${INSTALL_DIR} begin."
+  local output
+  if ! output="$(cp "${uninstallScript}" "${INSTALL_DIR}/uninstall.sh" 2>&1)"; then
+    toLogError "Failed to copy ${uninstallScript} to ${INSTALL_DIR}/uninstall.sh, output: ${output}"
+  else
+    setRightsForFiles "${INSTALL_DIR}/uninstall.sh" 755
+  fi
+  toLogInfo "Copy scripts ${uninstallScript} to ${INSTALL_DIR} done."
+
+  #== 复制临时目录 scripts/ 到安装目录 /opt/cloudwise/cwserveragent/scripts
+  copyScriptsToDirectory "${AGENT_SCRIPTS_DIR}"
+  if [ "${INIT_DIR}"x != "${AGENT_SCRIPTS_DIR}"x ]; then
+    copyScriptsToDirectory "${INIT_DIR}"
+  fi
+  toLogInfo "Copy scripts done."
+}
+
+#== 【4】=调用 agent 64位数lib路径(lib64/installaction)执行指令 --create-cluster-timestamp-file
+createFirstClusterTimestampFile() {
+  toLogInfo "Creating firstClusterTimestamp file"
+  #== 调用 agent 64位数lib路径(lib64/installaction)执行指令 --create-cluster-timestamp-file
+  "$(getAgentInstallActionPath)" "--create-cluster-timestamp-file" >>"${LOG_FILE}" 2>&1
+}
+
+#== 【4】=配置数据存储目录
+setupDataStorageDir() {
+  toLogInfo "Setup datastorage dir..."
+  local dataStorage
+  dataStorage="$(getValueFromConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${LEGACY_AGENT_CONF_FILE}" "${DATA_STORAGE_DIR}")"
+  if [ "${dataStorage}"x = "${LOG_DIR}"x ]; then
+    toLogInfo "Detected legacy data storage setting, changing it to the new default location"
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${DATA_STORAGE_DIR}" "${LEGACY_AGENT_CONF_FILE}"
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${DATA_STORAGE_DIR}" "${INSTALLER_CONF_FILE}"
+  fi
+
+  if [ "${PARAM_DATA_STORAGE}" ]; then
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${PARAM_DATA_STORAGE}" "${LEGACY_AGENT_CONF_FILE}"
+    writeParamToConfigFile "${CONF_FIELD_NM_DATA_STORAGE}" "${PARAM_DATA_STORAGE}" "${INSTALLER_CONF_FILE}"
+  fi
+
+  toLogInfo "Setup datastorage dir done."
+}
+
+#**********************************************************
+# Processing command line parameters
+#**********************************************************
+#== 【0】=help
+displayHelp() {
+  printf '%s\n' "Usage: $(basename "${INSTALLER_FILE}") [-h] [-v] "
+  #== printf '%s\n' "Usage: $(basename "${INSTALLER_FILE}") [-h] [-v] [DATA_SERVER=https://server_address:server_port] [CONFIG_SERVER=configService] [LICENSE=license] [INSTALL_DIR=install_path]"
+  local beginStr="Usage: "
+  local pad="${#beginStr}"
+
+  printf "\n\n"
+
+  pad=25
+  printf "%-${pad}s%s\n" "-h, --help" "Display this help and exit."
+  printf "%-${pad}s%s\n" "-v, --version" "Print version and exit."
+
+}
+
+printParamMessage() {
+  local paramNm="${1}"
+  local paramValue="${2}"
+  toConsoleInfo "---> Parameter ${paramNm}=${paramValue}."
+}
+
+printOtherParamMessage() {
+  local param="${1}"
+  toConsoleInfo "---> Parameter ${param}"
+}
+
+#== 【0】=大小写转化,并返回判断结果
+istrcmp() {
+  local s1
+  local s2
+  s1="$(printf '%s' "${1}" | tr '[:upper:]' '[:lower:]')"
+  s2="$(printf '%s' "${2}" | tr '[:upper:]' '[:lower:]')"
+  [ "${s1}"x = "${s2}"x ]
+}
+
+#== 【0】=
+isParamTrue() {
+  [ "${1}"x = "1"x ] || [ "${1}"x = "true"x ] || [ "${1}"x = "enable"x ] || [ "${1}"x = "yes"x ]
+}
+#== 【1】【5】=
+isParamFalse() {
+  [ "${1}"x = "0"x ] || [ "${1}"x = "false"x ] || [ "${1}"x = "disable"x ] || [ "${1}"x = "no"x ]
+}
+
+#== 【1】【5】=
+invertBoolValue() {
+  local valueToInvert="${1}"
+  if isParamFalse "${valueToInvert}"; then
+    printf '%s' "true"
+  else
+    printf '%s' "false"
+  fi
+}
+
+#== shellcheck disable=SC2003
+#== 【0】=获取参数值
+getParamValue() {
+  local paramName="${1}"
+  local input="${2}"
+  local paramNameLength="${#paramName}"
+  paramNameLength=$((paramNameLength + 1))
+  local partParam
+  partParam="$(expr substr "${input}" 1 ${paramNameLength})"
+#  partParam=$(substr "${input}" 1 ${paramNameLength})
+  if ! istrcmp "${partParam}" "${paramName}="; then
+    return 1
+  fi
+
+  local valueSeparator=$((paramNameLength + 1))
+  local value
+  value="$(expr substr "${input}" ${valueSeparator} 1000)"
+#  value=$(substr "${input}" ${valueSeparator} 1000)
+  if [ -z "${value}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${value}"
+  return 0
+}
+
+#== 【0】=获取bool参数
+readBoolParam() {
+  local value
+  value="$(getParamValue "${1}" "${2}")"
+
+  if value="$(getBoolParam "${value}")"; then
+    printf "%s" "${value}"
+    return 0
+  fi
+
+  return 1
+}
+
+#== 【0】=获取bool参数
+getBoolParam() {
+  local value="${1}"
+  if [ "${value}" ]; then
+    if isParamFalse "${value}"; then
+      printf "false"
+      return 0
+    fi
+    if isParamTrue "${value}"; then
+      printf "true"
+      return 0
+    fi
+  fi
+
+  return 1
+}
+
+#== 【0】=校验格式是否规范[1、大于4个字符且不能"cw."开头,2、不能超过100字符,3、只能包含字母数字字符,连字符,下划线和点。]
+validateParameter() {
+  local name="${1}"
+  local value="${2}"
+  if [ "$(printf '%s' "${value}" | cut -c -3)"x = "cw."x ]; then
+    toConsoleError "${name} must not begin with 'cw.'"
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "${#value}" -gt 100 ]; then
+    toConsoleError "Maximum allowed length of ${name} is 100."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if printf '%s' "${value}" | grep -q "[^[:alnum:]._-]"; then
+    toConsoleError "${name} can only contain alphanumeric characters, hyphen, underscore and dot."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】=验证用户和用户组是否匹配
+validateUserAndGroupParameters() {
+  local user="${1}"
+  local group="${2}"
+  local permittedNameRegex='^[[:alnum:]._][[:alnum:]._-]{2,31}$'
+
+  if [ ! "${group}" ]; then
+    group="${user}"
+  fi
+
+  if ! printf '%s' "${user}" | grep -qE "${permittedNameRegex}"; then
+    toConsoleError "USER can only contain alphanumeric characters, hyphen, underscore and dot, and must have length from 3 to 32 characters."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if ! printf '%s' "${group}" | grep -qE "${permittedNameRegex}"; then
+    toConsoleError "GROUP can only contain alphanumeric characters, hyphen, underscore and dot, and must have length from 3 to 32 characters."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  #== 校验用户/用户组信息
+  validateUserPrimaryGroup "${user}" "${group}"
+}
+
+#== 【0】【3】【5】=获取系统权限信息
+getSystemEntityInfo() {
+  local database="${1}"
+  local valueToCheck="${2}"
+
+  #== 校验命令getent是否存在
+  if ! isAvailable "getent"; then
+    toLogInfo "Command getent is not available"
+    return 2
+  fi
+
+  #== 查看系统权限
+  local output
+  output="$(getent "${database}" "${valueToCheck}" 2>&1)"
+  local returnCode=$?
+
+  if [ "${returnCode}" != 0 ]; then
+    if [ "${returnCode}" != 2 ]; then
+      toLogWarn "Failed to check ${valueToCheck} in ${database} database, message: ${output}, code: ${returnCode}"
+    fi
+    return 1
+  elif [ ! "${output}" ]; then
+    toLogWarn "Failed to get user information: getent returned no output"
+  fi
+
+  printf '%s' "${output}"
+  return 0
+}
+
+#== 【0】【3】=查看用户/用户组信息
+isEntityPassedById() {
+  local database="${1}"
+  local name="${2}"
+
+  local output
+  output="$(getSystemEntityInfo "${database}" "${name}")"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 0 ]; then
+    if [ ${returnCode} -eq 2 ]; then
+      toLogInfo "Installer will not be able to verify whether entity was passed by name or by ID"
+    fi
+    return 1
+  fi
+
+  local nameFromDatabase
+  nameFromDatabase="$(printf '%s' "${output}" | cut -d: -f1)"
+
+  if [ "${nameFromDatabase}"x = "${name}"x ]; then
+    return 1
+  fi
+
+  toLogWarn "Name from config and from ${database} system database do not match"
+  toLogWarn "Config: ${name}, database: ${nameFromDatabase}"
+  return 0
+}
+
+#== 【0】【3】【5】=用户是否存在
+userExistsInSystem() {
+  local user="${1}"
+
+  getSystemEntityInfo "passwd" "${user}" >/dev/null
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    return ${returnCode}
+  fi
+
+  toLogInfo "Trying to determine user existence using 'id' command"
+  id "${user}" >/dev/null 2>&1
+}
+
+#== 【5】= 查看group
+groupExistsInSystem() {
+  local group="${1}"
+
+  getSystemEntityInfo "group" "${group}" >/dev/null
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    return ${returnCode}
+  fi
+
+  toLogInfo "Installer will not be able to determine group existence"
+  return 1
+}
+
+#== 【3】=用户是否存在
+validateUserExistence() {
+  local user="${1}"
+  if ! userExistsInSystem "${user}"; then
+    toConsoleError "User named '${user}' configured for ${BRAND_PRODUCT_NAME} does not exist. Installation aborted."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】【3】=检查用户/用户组信息
+checkIfEntityWasNotPassedById() {
+  local database="${1}"
+  local valueToCheck="${2}"
+  local valueTypeToLog="user"
+
+  if [ "${database}"x = "group"x ]; then
+    valueTypeToLog="group"
+  fi
+
+  if isEntityPassedById "${database}" "${valueToCheck}"; then
+    toConsoleError "\"${valueToCheck}\" is not a ${valueTypeToLog} name but its ID. Installation aborted."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】【3】=用户权限下用户组ID
+getUserPrimaryGroupIdForComparison() {
+  local user="${1}"
+
+  local userPrimaryGroupId
+  userPrimaryGroupId="$(getSystemEntityInfo "passwd" "${user}")"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    printf '%s' "${userPrimaryGroupId}" | cut -d: -f4
+    return ${returnCode}
+  fi
+
+  toLogInfo "Returning user primary group name instead of its id"
+  id -gn "${user}"
+}
+
+#== 【0】【3】=获取用户组信息
+getGroupIdForComparison() {
+  local group="${1}"
+
+  local groupId
+  groupId="$(getSystemEntityInfo "group" "${group}")"
+  local returnCode=$?
+
+  if [ ${returnCode} -ne 2 ]; then
+    printf '%s' "${groupId}" | cut -d: -f3
+    return ${returnCode}
+  fi
+
+  toLogInfo "Returning group name instead of its id"
+  printf '%s' "${group}"
+}
+
+#== 【0】【3】=校验用户/用户组信息
+validateUserPrimaryGroup() {
+  local user="${1}"
+  local group="${2}"
+
+  if ! userExistsInSystem "${user}"; then
+    return
+  fi
+
+  checkIfEntityWasNotPassedById "passwd" "${user}"
+  checkIfEntityWasNotPassedById "group" "${group}"
+
+  #== 获取用户组ID
+  local groupId
+  groupId="$(getGroupIdForComparison "${group}")"
+  #== 获取用户下用户组ID
+  local userPrimaryGroupId
+  userPrimaryGroupId="$(getUserPrimaryGroupIdForComparison "${user}")"
+
+  toConsoleInfo "#DEBUG== group \"${group}\" id: \"${groupId}\", user \"${user}\" primaryGroupId: \"${userPrimaryGroupId}\""
+  if [ "${userPrimaryGroupId}"x != "${groupId}"x ]; then
+    toConsoleError "User named \"${user}\" does not have group named \"${group}\" as its primary group. Installation aborted."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【3】=检查用户/用户组是否存在和匹配
+checkUserAndGroupFromConfig() {
+  local configUser
+  local configGroup
+  configUser="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  configGroup="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+
+  toLogInfo "Checking validity of user account '${configUser}:${configGroup}'"
+
+  if [ "${PARAM_UPGRADE}"x = "yes"x ]; then
+    validateUserExistence "${configUser}"
+  fi
+
+  #== 校验用户/用户组信息
+  validateUserPrimaryGroup "${configUser}" "${configGroup}"
+}
+
+#== 【0】=解析命令行中参数
+parseCommandLineParameters() {
+  local dataServerIsEmpty=true
+  local configServerIsEmpty=true
+  while [ $# -gt 0 ]; do
+    local param="${1}"
+    local value=
+
+    if value=$(getParamValue DATA_SERVER "${param}"); then
+      PARAM_DATA_SERVER="${value}"
+      dataServerIsEmpty=false
+      printParamMessage "DATA_SERVER" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue CONFIG_SERVER "${param}"); then
+      PARAM_CONFIG_SERVER="${value}"
+      configServerIsEmpty=false
+      printParamMessage "CONFIG_SERVER" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue LICENSE "${param}"); then
+      PARAM_LICENSE="${value}"
+      printParamMessage "LICENSE" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue JSON_CONF "${param}"); then
+      PARAM_JSON_CONF="${value}"
+      printParamMessage "JSON_CONF" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue INSTALL_DIR "${param}"); then
+      PARAM_INSTALL_DIR="${value}"
+      printParamMessage "INSTALL_DIR" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue DATA_STORAGE "${param}"); then
+      PARAM_DATA_STORAGE="${value}"
+      printParamMessage "DATA_STORAGE" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(readBoolParam DISABLE_SYSTEM_LOGS_ACCESS "${param}"); then
+      PARAM_DISABLE_SYSTEM_LOGS_ACCESS="${value}"
+      printParamMessage "DISABLE_SYSTEM_LOGS_ACCESS" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue INTERNAL_OVERRIDE_CHECKS "${param}"); then
+      if printf '%s' "${value}" | grep -wq "privileges"; then
+        PARAM_INTERNAL_NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK=true
+      fi
+
+      if printf '%s' "${value}" | grep -wq "downgrade"; then
+        SKIP_DOWNGRADE_CHECK=true
+      fi
+      printParamMessage "INTERNAL_OVERRIDE_CHECKS" "${value}"
+
+      shift
+      continue
+    fi
+
+    if value=$(readBoolParam INTERNAL_USE_UNPACK_CACHE "${param}"); then
+      PARAM_INTERNAL_USE_UNPACK_CACHE="${value}"
+      printParamMessage "INTERNAL_USE_UNPACK_CACHE" "${value}"
+      shift
+      continue
+    fi
+
+    if [ "${ARCH_ARCH}"x != "AIX"x ]; then
+      if value=$(getParamValue USER "${param}"); then
+        PARAM_USER="${value}"
+        printParamMessage "USER" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(getParamValue GROUP "${param}"); then
+        PARAM_GROUP="${value}"
+        printParamMessage "GROUP" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam NON_ROOT_MODE "${param}"); then
+        PARAM_NON_ROOT_MODE="${value}"
+        printParamMessage "NON_ROOT_MODE" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam USER_LOGIN "${param}"); then
+        PARAM_USER_LOGIN="${value}"
+        printParamMessage "USER_LOGIN" "${value}"
+        shift
+        continue
+      fi
+
+      #== 自定义参数获取
+      if value=$(getParamValue TEST "${param}"); then
+        PARAM_TEST="${value}"
+        printParamMessage "TEST" "${value}"
+        shift
+        continue
+      fi
+
+      #== 自定义bool参数
+      if value=$(readBoolParam BOOL "${param}"); then
+        PARAM_BOOL="${value}"
+        printParamMessage "BOOL" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam DISABLE_ROOT_FALLBACK "${param}"); then
+        PARAM_DISABLE_ROOT_FALLBACK="${value}"
+        printParamMessage "DISABLE_ROOT_FALLBACK" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK "${param}"); then
+        PARAM_INTERNAL_NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK="${value}"
+        printParamMessage "NON_ROOT_MODE_SKIP_PRIVILEGES_CHECK" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam INTERNAL_SKIP_SELINUX_POLICY_INSTALLER "${param}"); then
+        #== PARAM_INTERNAL_SKIP_SELINUX_POLICY_INSTALLER="${value}"
+        printParamMessage "INTERNAL_SKIP_SELINUX_POLICY_INSTALLER" "${value}"
+        shift
+        continue
+      fi
+
+      if value=$(readBoolParam DOCKER_ENABLED "${param}"); then
+        #== PARAM_INTERNAL_DEPLOYED_VIA_CONTAINER="${value}"
+        printParamMessage "DOCKER_ENABLED" "${value}"
+        shift
+        continue
+      fi
+    fi
+
+    if [ "${ARCH_ARCH}"x != "AIX"x ] && [ "${ARCH_ARCH}"x != "S390"x ]; then
+      if value=$(readBoolParam INTERNAL_DISABLE_DUMPPROC "${param}"); then
+        #== PARAM_INTERNAL_DISABLE_DUMPPROC="${value}"
+        printParamMessage "INTERNAL_DISABLE_DUMPPROC" "${value}"
+        shift
+        continue
+      fi
+    fi
+
+    if [ "${param}"x = "-h"x ] || [ "${param}"x = "--help"x ]; then
+      displayHelp
+      finishInstallation "${EXIT_CODE_OK}"
+    fi
+
+    if [ "${param}"x = "-v"x ] || [ "${param}"x = "--version"x ]; then
+      printf "%s\n" "${AGENT_INSTALLER_VERSION}"
+      finishInstallation "${EXIT_CODE_OK}"
+    fi
+    
+    printOtherParamMessage ${param}
+    shift
+  done
+
+  if [ "${configServerIsEmpty}"x = "true"x ] && [ "${dataServerIsEmpty}"x != "true"x ]; then
+    PARAM_CONFIG_SERVER="${PARAM_DATA_SERVER}"
+  fi
+}
+
+#== 【0】=校验PARAM_INSTALL_DIR是否规范【1、不能包含空格,2、不能在根目录,3、安装路径必须是绝对的】
+validateInstallPathParameter() {
+  if printf '%s' "${PARAM_INSTALL_DIR}" | grep -q "[[:space:]]"; then
+    toConsoleError "Installation path must not contain spaces."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "${PARAM_INSTALL_DIR}"x = "/"x ]; then
+    toConsoleError "Installation path must not point to the filesystem root directory."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "$(printf '%s' "${PARAM_INSTALL_DIR}" | cut -c 1)"x != "/"x ]; then
+    toConsoleError "Installation path must be absolute."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+#== 【0】=校验PARAM_DATA_STORAGE是否规范【1、不能包含空格,2、不能在根目录,3、安装路径必须是绝对的,4、数据目录不能放在安装目录下】
+validateDataStorageParameter() {
+  if printf '%s' "${PARAM_DATA_STORAGE}" | grep -q "[[:space:]]"; then
+    toConsoleError "Data storage path must not contain spaces."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "${PARAM_DATA_STORAGE}"x = "/"x ]; then
+    toConsoleError "Data storage path must not point to the filesystem root directory."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if [ "$(printf '%s' "${PARAM_DATA_STORAGE}" | cut -c 1)"x != "/"x ]; then
+    toConsoleError "Data storage path must be absolute."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+
+  if printf '%s' "${PARAM_DATA_STORAGE}" | grep -q "^${INSTALL_DIR}"; then
+    toConsoleError "Data storage path must not be located within ${INSTALL_DIR}."
+    finishInstallation "${EXIT_CODE_INVALID_PARAM}"
+  fi
+}
+
+
+#**********************************************************
+# Config files
+#**********************************************************
+
+#== 【3】【5】=格式化空间大小(将 1024KiB 格式化成 1MiB, 将 1024MiB 格式化成 1GiB,...)
+formatSize() {
+  local sizeInKiB="${1}"
+  local formattedSize
+
+  for symbol in "KiB" "MiB" "GiB" "TiB"; do
+    if printf '%s' "${sizeInKiB}" | awk '$1 >= 1024 { exit 1; }'; then
+      formattedSize="${sizeInKiB} ${symbol}"
+      break
+    fi
+    sizeInKiB="$(printf '%s' "${sizeInKiB}" | awk '{ print $1 / 1024 }')"
+  done
+
+  printf '%s' "${formattedSize}"
+}
+
+#== 【3】【5】=
+cropSizeValue() {
+  local size="${1}"
+  local value
+  local unit
+  value="$(printf '%s' "${size}" | cut -d' ' -f1)"
+  unit="$(printf '%s' "${size}" | cut -d' ' -f2)"
+  printf '%.2f %s' "${value}" "${unit}"
+}
+
+#== 【3】【5】=检查目录可用空间(单位:k)
+checkFreeSpace() {
+  local path="${1}"
+  local requiredSpaceInKiB="${2}"
+  local dfOutput
+  local baseFilesystem
+  local freeSpace
+  local formattedRequiredSpace
+  local formattedFreeSpace
+
+  #== shellcheck disable=SC2086
+  dfOutput="$(df -P ${AIX_DF_SPECIFIC_FLAG} "${path}" | tail -n +2)"
+  #== 文件系统目录
+  baseFilesystem="$(printf "%s" "${dfOutput}" | awk '{ print $NF }')"
+  #== 剩余空间(单位:k)
+  freeSpace="$(printf "%s" "${dfOutput}" | awk '{ print $4 }')"
+
+  #== 格式化空间大小(将 1024KiB 格式化成 1MiB, 将 1024MiB 格式化成 1GiB,...)
+  formattedRequiredSpace="$(formatSize "${requiredSpaceInKiB}")"
+  toLogInfo "Filesystem with ${path} is mounted under ${baseFilesystem}. Space required: ${formattedRequiredSpace}."
+
+  if [ ! "${freeSpace}" ]; then
+    printf 'Cannot determine amount of free space on %s (filesystem mounted under %s)' "${path}" "${baseFilesystem}"
+    return 1
+  fi
+
+  #== 格式化空间大小(将 1024KiB 格式化成 1MiB, 将 1024MiB 格式化成 1GiB,...)
+  formattedFreeSpace="$(formatSize "${freeSpace}")"
+  toLogInfo "Available free space: ${formattedFreeSpace}"
+
+  if [ "${freeSpace}" -lt "${requiredSpaceInKiB}" ]; then
+    printf 'Not enough free space on %s (filesystem mounted under %s). ' "${path}" "${baseFilesystem}"
+    printf 'Required: %s, available: %s' "$(cropSizeValue "${formattedRequiredSpace}")" "$(cropSizeValue "${formattedFreeSpace}")"
+    return 2
+  fi
+
+  printf 'Free space is sufficient'
+  return 0
+}
+
+#== 【3】=检查文件系统类型
+
+#== 【3】=检查安装目录空间
+checkInstallPathFreeSpace() {
+  local externalTarSize=${EXTERNAL_TAR_SIZE}
+  local artifactsSize=${ARTIFACTS_SIZE}
+  #== use 10% additional margin
+  local requiredSpace=$((externalTarSize + artifactsSize * 11 / 10))
+  #== convert to kibibytes
+  requiredSpace=$((requiredSpace / 1024))
+
+  toConsoleInfo "Checking free space in ${INSTALL_DIR}"
+
+  local message
+  #== 检查目录可用空间(单位:k)
+  message="$(checkFreeSpace "${INSTALL_DIR}" "${requiredSpace}")"
+  case $? in
+  0) toLogInfo "${message}" ;;
+  1) toConsoleWarn "${message}. Installation may be incomplete." ;;
+  2)
+    toConsoleError "${message}"
+    finishInstallation "${EXIT_CODE_NOT_ENOUGH_SPACE}"
+    ;;
+  esac
+}
+
+#== 【3】=获取指定路径的文件系统信息
+getFilesystemInfo() {
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    mount | grep " ${1} "
+  else
+    grep " ${1} " /proc/self/mounts
+  fi
+}
+
+#== 【3】=检查目录指定权限信息
+checkAccessRightsTo() {
+  local dir="${1}"
+  toLogInfo "Checking access to ${dir}..."
+  #== 获取文件权限信息
+  local accessRights
+  accessRights="$(arch_getAccessRights "${dir}" | cut -c 2-4)"
+  if ! printf '%s' "${accessRights}" | grep -q rwx; then
+    toConsoleError "Insufficient permissions on ${dir}: '${accessRights}'."
+    toLogInfo "$(ls -dl "${dir}" 2>&1)"
+    finishInstallation "${EXIT_CODE_INSUFFICIENT_PERMISSIONS}"
+  fi
+
+  local dfResult
+  local filesystem
+  local filesystemInfo
+  dfResult="$(df -P "${dir}")"
+  #== 获取目录文件系统路径
+  filesystem="$(printf '%s' "${dfResult}" | tail -1 | awk '{ print $NF }')"
+  #== 获取指定路径的文件系统信息
+  filesystemInfo="$(getFilesystemInfo "${filesystem}")"
+  if ! printf '%s' "${filesystemInfo}" | grep -qw rw; then
+    toLogWarn "df-based check determined filesystem mounted under ${filesystem} as readonly, trying fallback."
+    toLogWarn "Filesystem access rights: '${filesystemInfo}'"
+    toLogWarn "df returned: ${dfResult}"
+
+    if ! testWriteAccessToDir "${dir}"; then
+      toConsoleError "readonly filesystem mounted under ${filesystem}"
+      finishInstallation "${EXIT_CODE_INSUFFICIENT_PERMISSIONS}"
+    fi
+  fi
+
+  toLogInfo "Rights on directory ${dir} are sufficient"
+} 2>>"${LOG_FILE}"
+
+#== 【3】=目录是否写权限
+checkIfInstallationPathIsWriteable() {
+  for dir in "${@}"; do
+    if [ -d "${dir}" ]; then
+      checkAccessRightsTo "${dir}"
+      break
+    fi
+  done
+}
+
+#== 【3】=检查目录权限
+checkAccessRightsToDirs() {
+  checkIfInstallationPathIsWriteable "${INSTALL_DIR}" "${BASE_INSTALL_DIR}" "${INSTALL_BASE}" /
+
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+    checkAccessRightsTo "${INIT_DIR}"
+  fi
+}
+
+
+#== 【0】=检查系统是 LINUX、AIX及指令集(X86_64\IA64\X86)
+checkSystemCompatibility() {
+  local expectedPlatform="LINUX"
+  if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+    expectedPlatform="AIX"
+  fi
+
+  #== 获取系统名称
+  local platform
+  platform="$(uname | sed -e 's/_.*//' | sed -e 's/\///' | tr '[:lower:]' '[:upper:]')"
+  if [ "${platform}"x != "${expectedPlatform}"x ]; then
+    printf "Cannot determine platform or platform not supported: <%s>" "${platform}"
+    return 1
+  fi
+
+  #== 检查系统指令集(X86_64\IA64\X86)
+  local detectedArchitecture
+  if ! detectedArchitecture="$(arch_checkArchitectureCompatibility)"; then
+    printf "Cannot determine architecture or architecture not supported: <%s>" "${detectedArchitecture}"
+    return 1
+  fi
+
+  printf 'Detected platform: %s' "${platform}"
+  if [ "${detectedArchitecture}" ]; then
+    printf ' arch: %s' "${detectedArchitecture}"
+  fi
+
+  return 0
+}
+
+#== 【4】=从sh文件读取压缩文件 tarfile_$$.base64
+separateExternalTar() {
+  toLogInfo "Determining begin of tar archive..."
+  local tarBegin
+  local tarEnd
+  #== 压缩文件开始行数,占位符为【#################ENDOFSCRIPTMARK############】
+  tarBegin="$(locateDelimiter "#################ENDOFSCRIPTMARK############" "")"
+  tarBegin=$((tarBegin + 1))
+  #== 压缩文件结束行数,占位符为【----SIGNED-INSTALLER】
+  tarEnd="$(locateDelimiter "----SIGNED-INSTALLER" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS})"
+  toLogInfo "tarBegin=${tarBegin} tarEnd=${tarEnd}"
+
+  if [ ! "${tarEnd}" ]; then
+    toConsoleError "S/MIME signature is missing, installation package is corrupted."
+    finishInstallation "${EXIT_CODE_CORRUPTED_PACKAGE}"
+  fi
+
+  local tarLength=$((tarEnd - tarBegin))
+  toLogInfo "tarLength=${tarLength}"
+  #== 从sh文件读取压缩文件 tarfile_$$.base64
+  tail -n +"${tarBegin}" "${INSTALLER_FILE}" 2>/dev/null | head -${tarLength} >"${EXTERNAL_TAR_FILE}"
+}
+
+#== 【4】【5】=进入目录
+changeWorkingDir() {
+  if ! cd "${1}"; then
+    toLogError "Failed to change working directory to ${1}"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== shellcheck disable=SC2181
+#== 【4】=解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+unpackArchiveWithoutCache() {
+  #== 解压文件编码
+  local base64Binary="${UNPACK_BINARY}"
+  #== 解压文件编码参数
+  local base64BinaryArgs="${UNPACK_BINARY_ARGS}"
+  #== 使用xzdec命令可以进行liblzma为基础的xz文件解压缩
+  local xzBinary="${INSTALL_DIR}/xzdec"
+  #== 进入目录
+  changeWorkingDir "${INSTALL_DIR}"
+
+  if ! isAvailable tar; then
+    toConsoleError "tar binary not found. Setup can't continue"
+    finishInstallation "${EXIT_CODE_MISCONFIGURED_ENVIRONMENT}"
+  fi
+
+  local totalLines
+  if ! isAvailable "${base64Binary}"; then
+    toLogInfo "${base64Binary} not found. Falling back to openssl decode"
+    if ! isAvailable openssl; then
+      toConsoleError "Neither ${base64Binary} nor openssl can be found. Setup can't continue"
+      finishInstallation "${EXIT_CODE_MISCONFIGURED_ENVIRONMENT}"
+    fi
+    #== 如果没有 base64 命令,则使用 openssl 解压文件
+    base64Binary="openssl"
+    base64BinaryArgs="enc -base64 -d -in"
+
+    if [ "${ARCH_ARCH}"x = "AIX"x ]; then
+      #truncate the first and the last one line due to specific format of uuencode on aix
+      totalLines="$(wc -l "${EXTERNAL_TAR_FILE}" | awk '{print $1}')"
+      head -$((totalLines - 1)) "${EXTERNAL_TAR_FILE}" 2>/dev/null | tail +2 >"${EXTERNAL_TAR_FILE}.$$"
+      mv -f "${EXTERNAL_TAR_FILE}.$$" "${EXTERNAL_TAR_FILE}"
+    fi
+  fi
+
+  {
+    #== base64 转编码后,执行 tar 解压
+    "${base64Binary}" "${base64BinaryArgs}" "${EXTERNAL_TAR_FILE}" | tar -x -p -f -
+  } 2>>"${LOG_FILE}"
+
+  #== base64 转编码 或 tar解压失败
+  if [ $? -gt 0 ]; then
+    toConsoleError "Archive is corrupted or memory allocation failed. Installation aborted."
+    finishInstallation "${EXIT_CODE_NOT_ENOUGH_MEMORY}"
+  fi
+
+  #== 进入目录
+  changeWorkingDir "${TMP_DIR}"
+  {
+    #== 使用xzdec命令解压后,执行 tar 解压
+    "${xzBinary}" "${INTERNAL_TAR_FILE}" | tar -x -p -f -
+  } 2>>"${LOG_FILE}"
+
+  if [ $? -gt 0 ]; then
+    toConsoleError "XZ compressed archive is corrupted or memory allocation failed. Installation aborted."
+    finishInstallation "${EXIT_CODE_NOT_ENOUGH_MEMORY}"
+  fi
+  #== 进入目录
+  changeWorkingDir "${INSTALL_DIR}"
+}
+
+#== 【4】=解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+unpackArchive() {
+  toConsoleInfo "Unpacking. This may take a while..."
+
+  #== 【参数】是否使用解压缓存
+  if [ "${PARAM_INTERNAL_USE_UNPACK_CACHE}"x = "true"x ]; then
+    if [ -d "${UNPACK_CACHE}" ]; then
+      toLogInfo "Unpack cache will be used."
+      cp -Rp "${UNPACK_CACHE}"/* "${TMP_DIR}"
+    else
+      toLogInfo "Unpack cache does not exist."
+      mkdir -p "${UNPACK_CACHE}"
+      #== 解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+      unpackArchiveWithoutCache
+      cp -Rp "${TMP_DIR}"/* "${UNPACK_CACHE}"
+    fi
+  else
+    toLogInfo "Unpacking without cache"
+    #== 解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+    unpackArchiveWithoutCache
+  fi
+
+  toConsoleInfo "Unpacking complete."
+}
+
+#== 【3】=检查容器部署、运行情况、配置参数
+#isDeployedInsideOpenVZContainer
+#isProcessRunningInContainer
+#isDeployedViaContainer
+
+
+#== Checking if libc is new enough
+#== 【3】=格式化version
+format_version() {
+  printf '%s' "$@" | awk -F. '{ printf("%03d%03d%03d\n", $1,$2,$3); }'
+}
+
+#== 【3】=格式化路径
+format_patch() {
+  printf '%s' "$@" | tail -n 1 | awk -F- '{ printf("%d\n", $3); }'
+}
+
+#== 【3】=检查是否降级安装
+checkIfDowngrade() {
+  if [ "${SKIP_DOWNGRADE_CHECK}"x = "true"x ]; then
+    toConsoleInfo "Skipped downgrade check"
+    return
+  fi
+
+  local installVersionFile="${INSTALL_DIR}/installer.version"
+  if [ ! -f "${installVersionFile}" ]; then
+    toLogWarn "Could not perform downgrade check, ${installVersionFile} file is missing"
+    return
+  fi
+
+  local oldVersion
+  oldVersion="$(cat "${installVersionFile}")"
+  if [ "$(format_version "${AGENT_INSTALLER_VERSION}")" -eq "$(format_version "${oldVersion}")" ]; then
+    toConsoleError "${BRAND_PRODUCT_NAME} is already installed, please uninstall the old version using [${INSTALL_DIR}/uninstall.sh]."
+#    toLogError "Attempted downgrade from ${oldVersion} to ${AGENT_INSTALLER_VERSION}"
+    finishInstallation "${EXIT_CODE_UNSUPPORTED_DOWNGRADE}"
+  fi
+}
+
+#== 【3】=检查是否已经安装
+checkIfAlreadyInstalled() {
+  if [ -f "${INSTALL_DIR}/uninstall.sh" ] && [ -f "${AGENT_BIN_DIR}/${AGENT_PROC}" ] ; then
+#    toConsoleError "Agent already installed. Uninstalling previous version."
+#    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+
+    checkIfDowngrade
+    PARAM_UPGRADE="yes"
+  else
+#  		if [ -f "${SIF_AGENT_INSTALL_PATH}/lib64/${AGENT_BIN}" ]; then
+#  			sif_toConsoleError "Upgrade is not possible because uninstall script is missing"
+#  			finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+#  		fi
+      local fileNum
+      fileNum=`ls ${INSTALL_DIR} | wc -l`
+      if [ "${fileNum}" -gt 1 ];then
+         toConsoleError "${INSTALL_DIR} is not empty. Please clean or change the installation directory manually."
+         finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+      fi
+  fi
+
+
+
+}
+
+#**********************************************************
+# Init related functions
+#**********************************************************
+
+#== clears dependencies in LSB init script
+#== 【5】=清除LSB init脚本中的依赖项
+clearDependenciesInLSBInit() {
+  local file="${1}"
+  toConsoleInfo "Clearing dependencies in file ${file}"
+  awk '
+		BEGIN {
+			req_start_found=0;
+			req_stop_found=0;
+			REQ_START="# Required-Start:";
+			REQ_STOP="# Required-Stop:";
+			PATTERN_REQ_START="^" REQ_START;
+			PATTERN_REQ_STOP="^" REQ_STOP;
+		}
+		{
+			if ($0 ~ PATTERN_REQ_START && req_start_found == 0) {
+				print REQ_START;
+				req_start_found++;
+			} else if ($0 ~ PATTERN_REQ_STOP && req_stop_found == 0) {
+				print REQ_STOP;
+				req_stop_found++;
+			} else
+				print $0
+		}' "${file}" >"${file}.tmp" && mv -f "${file}.tmp" "${file}"
+
+  chmod +x "${file}"
+}
+
+#== 【5】=指定目录添加自动启动脚本
+addScriptToSystemvAutostart() {
+  local prefix="${1}"
+  local file="${2}"
+  local suffix="${3}"
+
+  toLogInfo "Adding ${file} to autostart"
+  #== 自动启动工具
+  if ! runAutostartAddingTool "${prefix}" "${file}" "${suffix}"; then
+    toLogWarn "Failed to add ${file} script to autostart. Trying without dependencies..."
+    #== 清除LSB init脚本中的依赖项
+    clearDependenciesInLSBInit "${INIT_DIR}/${file}"
+    if ! runAutostartAddingTool "${prefix}" "${file}" "${suffix}"; then
+      toConsoleError "Cannot add ${file} to autostart. For details, see: ${LOG_FILE}"
+    fi
+  fi
+}
+
+#== 【5】=指定目录添加自动启动脚本
+addScriptsToAutostart() {
+  local prefix="${1}"
+  local suffix="${2}"
+  #== 指定目录添加自动启动脚本
+  addScriptToSystemvAutostart "${prefix}" "${SERVICE_SCRIPT_FILE}" "${suffix}"
+}
+
+#== 【5】=设置自动启动
+#setupSystemvAutostart() {
+#  toLogInfo "Adding ${BRAND_AGENT_PRODUCT_NAME} to autostart..."
+#
+#  if [ -x /usr/bin/update-rc.d ]; then #Ubuntu
+#    #== 指定目录添加自动启动脚本
+#    addScriptsToAutostart "/usr/bin/update-rc.d " "defaults"
+#  elif [ -x /usr/sbin/update-rc.d ]; then #Ubuntu
+#    addScriptsToAutostart "/usr/sbin/update-rc.d " "defaults"
+#  elif [ -x /sbin/chkconfig ]; then #RedHat
+#    addScriptsToAutostart "/sbin/chkconfig --add "
+#  elif [ -x /usr/lib/lsb/install_initd ]; then #Suse
+#    addScriptsToAutostart "/usr/lib/lsb/install_initd ${INIT_DIR}/"
+#  elif [ "${ARCH_ARCH}"x = "AIX"x ]; then
+#    #== 【不存在】
+#    arch_setAutostart
+#  else
+#    toConsoleError "Couldn't add ${BRAND_AGENT_PRODUCT_NAME} to autostart. Please adjust and add it manually."
+#  fi
+#}
+
+#== 【5】=设置 cwserveragent.service 中用户
+setServiceScriptUser() {
+  if ! isNonRootModeEnabled; then
+    return
+  fi
+  #== 读取用户名(调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-user)
+  local user
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 修改 cwserveragent.service 中用户信息
+  sed -i "s/User=.*/User=${user}/g" "${SYSTEMD_UNIT_FILES_DIR}/${SYSTEMD_UNIT_FILE_AGENT}"
+}
+
+#== 【5】=配置自动启动 cwserveragent.service
+setupSystemdAutostart() {
+  mv -f "${AGENT_SCRIPTS_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" "${SYSTEMD_UNIT_FILES_DIR}/"
+  setRightsForFiles "${SYSTEMD_UNIT_FILES_DIR}/${SYSTEMD_UNIT_FILE_AGENT}" 644
+
+  #== 设置 cwserveragent.service 中用户
+  setServiceScriptUser
+
+  if isAvailable restorecon; then
+    restorecon "${SYSTEMD_UNIT_FILES_DIR}/${SYSTEMD_UNIT_FILE_AGENT}"
+  fi
+
+  #== 执行 systemctl 命令配置允许开机启动 cwserveragent.service
+  executeSystemctlCommand enable "${SYSTEMD_UNIT_FILE_AGENT}"
+  #== 执行 systemctl 命令重新加载模块
+  executeSystemctlCommand daemon-reload ""
+}
+
+#== 【6】=运行service 脚本
+execIntoServiceScript() {
+  toConsoleInfo "${SERVICE_SCRIPT_FILE} will be started via exec()"
+  #== 清除安装临时文件
+  cleanInstallationTemporaryFiles
+  toLogInfo "Installation finished, PID $$."
+
+  local command="exec ${AGENT_SCRIPTS_DIR}/${SERVICE_SCRIPT_FILE} exec"
+  toLogInfo "Executing: ${command}"
+  #== 执行 cwserveragent exec
+  ${command}
+
+  toLogError "Could not execute: ${command}"
+  finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+}
+
+#== 【6】=运行agent
+runAgents() {
+  toConsoleInfo "Starting agents..."
+
+  toLogInfo "Using ${INIT_SYSTEM} to start the agent"
+  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+    #== 运行初始化命令(通过 service start 方式 或 直接运行可执行命令)
+    executeInitScriptCommand start "true"
+  else
+    #== 执行 systemctl start 命令
+    executeSystemctlCommand start "${SYSTEMD_UNIT_FILE_AGENT}"
+  fi
+
+  if [ $? -eq 0 ]; then
+    toConsoleInfo "${SERVICE_SCRIPT_FILE} service started"
+  else
+    toConsoleError "Failed to start service: ${SERVICE_SCRIPT_FILE}, it is possible that your init system is not functioning properly. For details, see: ${LOG_FILE}"
+  fi
+}
+
+#== 【5】=配置自动启动
+#setupAutostart() {
+#  if [ "${INIT_SYSTEM}"x = "${INIT_SYSTEM_SYSV}"x ]; then
+#    #== 设置自动启动
+#    setupSystemvAutostart
+#  else
+#    #== 配置自动启动 cwserveragent.service
+#    setupSystemdAutostart
+#  fi
+#}
+
+#**********************************************************
+# Process agent related functions
+#**********************************************************
+
+#== 【5】=创建 agent 状态文件
+createAgentStateFile() {
+  local path="${1}"
+  local agentStateContents="RUNNING"
+
+  toLogInfo "Writing ${agentStateContents} to ${path} file"
+  {
+    printf "%s" "${agentStateContents}" >"${path}.tmp"
+    mv -f "${path}.tmp" "${path}"
+  } 2>>"${LOG_FILE}"
+}
+
+#== 【0】=检查全路径是否可读权限(目录最深100层)
+checkIfPathIsGloballyReadable() {
+  local path="${1}"
+  local sourcePath="${path}"
+  local maxDepth=100
+  while [ "${path}"x != "/"x ]; do
+    #== 【0】=获取文件权限信息
+    local accessRights
+    accessRights="$(arch_getAccessRights "${path}")"
+    if ! printf '%s' "${accessRights}" | cut -c 8-10 | grep -qE "r.[xt]"; then
+      toConsoleError "Insufficient access rights (${accessRights}) on: ${path}"
+      toConsoleError "${sourcePath} path must be globally readable (r-x permissions for others)."
+      toConsoleError "Please adjust the permissions and then retry the installation."
+      finishInstallation "${EXIT_CODE_INSUFFICIENT_PERMISSIONS}"
+    fi
+
+    path="$(dirname "${path}")"
+
+    maxDepth=$((maxDepth - 1))
+    if [ "${maxDepth}" -eq 0 ]; then
+      toLogWarn "Unable to verify access rights on ${path}"
+      return
+    fi
+  done
+}
+
+#== 【0】=创建 PARAM_INSTALL_DIR -> INSTALL_DIR 软连接
+createSymlinkToInstallLocation() {
+  #== INSTALL_DIR 不能是软链接地址
+  if [ -L "${INSTALL_DIR}" ] && [ ! -e "${INSTALL_DIR}" ]; then
+    toConsoleError "Detected that ${INSTALL_DIR} is a dangling symlink, please remove it and then retry the installation"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== PARAM_INSTALL_DIR 和 INSTALL_DIR 都不能是空地址
+  if [ "${PARAM_INSTALL_DIR}"x = "${INSTALL_DIR}"x ] || [ -z "${PARAM_INSTALL_DIR}" ]; then
+    return
+  fi
+
+  #== INSTALL_DIR 是软连接,并获取 PARAM_INSTALL_DIR 和 INSTALL_DIR 的真实地址
+  if [ -L "${INSTALL_DIR}" ] && [ "$(readLink -m "${PARAM_INSTALL_DIR}")"x = "$(readLink -m "${INSTALL_DIR}")"x ]; then
+    return
+  fi
+
+  #== 目录存在,表示未卸载
+  if [ -e "${INSTALL_DIR}" ]; then
+    toConsoleError "Leftovers from previous agent installation detected"
+    toConsoleError "If you wish to use INSTALL_DIR parameter then perform a cleanup by following these steps:"
+    toConsoleError "1. Uninstall the agent"
+    toConsoleError "2. Restart all applications that have Deep Monitoring enabled (host restart is fine as well)"
+    toConsoleError "3. Remove ${INSTALL_DIR}"
+    toConsoleError "and then retry the installation."
+    toConsoleError "#DEBUG==For further information please visit ${HELP_URL}/command-line-install"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 创建不存在的目录
+  createDirIfNotExistAndSetRights "${PARAM_INSTALL_DIR}" 1775
+  #== 检查全路径是否可读权限(目录最深100层)
+  checkIfPathIsGloballyReadable "${PARAM_INSTALL_DIR}"
+  #== 创建不存在的目录 /opt/cloudwise
+  createDirIfNotExistAndSetRights "${BASE_INSTALL_DIR}" 755
+
+  #== 创建 PARAM_INSTALL_DIR -> INSTALL_DIR
+  local lnOutput
+  if lnOutput="$(ln -fs "${PARAM_INSTALL_DIR}" "${INSTALL_DIR}" 2>&1)"; then
+    toConsoleInfo "Symlink ${INSTALL_DIR} -> ${PARAM_INSTALL_DIR} created"
+  else
+    toConsoleError "Failed to create symlink ${INSTALL_DIR} -> ${PARAM_INSTALL_DIR}, aborting installation: ${lnOutput}"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== 【5】=修改文件系统用户和用户组
+changeFilesOwnership() {
+  local user
+  local group
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  group="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+
+  toLogInfo "Changing ownership of files to root:${group}"
+  #== /opt/cloudwise/cwserveragent 目录下用户和用户组
+  commandErrorWrapper chown "${user}:${group}" "${AGENT_BASE_DIR}"
+  commandErrorWrapper chown -R "${user}:${group}" "${INSTALL_DIR}"
+  toLogInfo "Recursively changing group ownership of ${AGENT_CONF_DIR} to ${group}"
+
+}
+
+#== 【5】=文件用户权限检查
+fileCapabilitiesCompatibilityCheck() {
+  #== 获取配置用户
+  local user
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 获取OS bin配置路径 /opt/cloudwise/cwserveragent/lib64/cloudwiseosconfig
+  if output="$("$(getOsConfigBinPath)" file-capabilities-compatibility-check "${user}" 2>&1)"; then
+    return 0
+  fi
+
+  if [ "${PARAM_NON_ROOT_MODE}" ]; then
+    toConsoleWarn "Failed to enable non-privileged mode, kernel does not support file capabilities. For details, see: ${LOG_FILE}"
+    toLogWarn "Capabilities test output: ${output}"
+  else
+    toConsoleInfo "Non-privileged mode was not enabled, kernel does not support file capabilities. For details, see: ${LOG_FILE}"
+    toLogInfo "Capabilities test output: ${output}"
+  fi
+  #== 设置删除root权限
+  "$(getAgentInstallActionPath)" "--set-drop-root-privileges" "false" >>"${LOG_FILE}" 2>&1
+  return 1
+}
+
+
+#**********************************************************
+# User and group related functions
+#**********************************************************
+
+#== 【5】=添加用户组
+addGroup() {
+  local group="${1}"
+
+  if groupExistsInSystem "${group}"; then
+    toLogInfo "Group '${group}' already exists"
+    return 0
+  fi
+
+  local errorMessage
+  errorMessage="$(groupadd "${group}" 2>&1)"
+  local returnCode=$?
+
+  case ${returnCode} in
+  0) toLogInfo "Group '${group}' successfully created" ;;
+  9) toLogInfo "Group '${group}' already exists" ;;
+  *)
+    toLogError "Error occured while adding '${group}' group, return code: ${returnCode}, message ${errorMessage}"
+    return 1
+    ;;
+  esac
+  return 0
+}
+
+#== 【5】=添加用户
+addUser() {
+  local user="${1}"
+  local group="${2}"
+  local groupCreated="${3}"
+  local errorMessage
+  local mod="/bin/false"
+  if [ "${PARAM_USER_LOGIN}"x == "true"x ]; then
+    mod="/bin/bash"
+  fi
+  if userExistsInSystem "${user}"; then
+    toLogInfo "User '${user}' already exists."
+    #-- 权限更新
+#    errorMessage="$(usermod -s "${mod}" "${user}" 2>&1)"
+    return 0
+  fi
+
+  if [ "${groupCreated}" -eq 0 ]; then
+    errorMessage="$(useradd -r --shell "${mod}" -g "${group}" "${user}" 2>&1)"
+  else
+    errorMessage="$(useradd -r --shell "${mod}" "${user}" 2>&1)"
+  fi
+
+  local returnCode=$?
+  if [ ${returnCode} -ne 0 ]; then
+    toConsoleError "Failed to create user '${user}'"
+    toLogError "Error occured while adding '${user}' user, return value: ${returnCode}, error message: ${errorMessage}."
+    return 1
+  fi
+
+  toConsoleInfo "User '${user}' added successfully."
+  return 0
+}
+
+#== 【5】=添加用户和用户组信息
+addUserAndGroup() {
+  local user="${1}"
+  local group="${2}"
+  #== 添加用户组
+  addGroup "${group}"
+  #== 添加用户
+  addUser "${user}" "${group}" $?
+}
+
+#== 【5】=配置用户和用户组
+handleUser() {
+  toLogInfo "Processing user and group..."
+  local user
+  local group
+  #== 读取用户名(调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-user)
+  user="$(getValueFromConfigFile "${CONF_FIELD_NM_USER}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 读取用户组(调用 agent 64位数lib路径(lib64/installaction)执行指令 --get-group)
+  group="$(getValueFromConfigFile "${CONF_FIELD_NM_GROUP}" "${LEGACY_AGENT_CONF_FILE}" "${BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME}")"
+  #== 添加用户和用户组信息
+  addUserAndGroup "${user}" "${group}"
+}
+
+#== 【4】=获取agent preLoad 安装路径,并校验是否可执行
+checkCompatibilityWithInstallActionBinary() {
+  local output
+  #== 获取agent preLoad 安装路径,并校验是否可执行
+  output="$("$(getAgentInstallActionPathPreInstallation)" "--sanity-check" 2>&1)"
+  local exitCode=$?
+  toLogAdaptive ${exitCode} "Compatibility check exit code = ${exitCode}, output = ${output}"
+  if [ ${exitCode} -ne 0 ] || [ "${output}"x != "SUCCESS"x ]; then
+    toConsoleError "System compatibility check failed, this may be caused by a problem with glibc, dynamic loader or incompatible operating system version."
+    toConsoleError "Detected version: $(detectLinuxDistribution)"
+    toConsoleError "For a list of supported distributions and versions, see: ${HELP_URL}"
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== 【4】=【部署】解压文件
+extractFiles() {
+  umask 000
+  toConsoleInfo "Extracting..."
+  #== 从sh文件读取压缩文件 tarfile_$$.base64
+  separateExternalTar
+  #== 解压文件(1、通过base64转码后,执行tar解压;2、通过xzdec解压xz文件后在执行tar解压)
+  unpackArchive
+  umask 022
+}
+
+#== 【3】=检查发现初始化系统信息(INIT_SYSTEM、INIT_SYSTEM_VERSION、INIT_DIR)
+detectInitSystem() {
+  #== 获取初始化系统信息 (INIT_SYSTEM、INIT_SYSTEM_VERSION)
+  checkInitSystem
+  toLogInfo "Detected init system: ${INIT_SYSTEM}, version: ${INIT_SYSTEM_VERSION}"
+  #== 设置系统初始化脚本目录
+  if ! setLocationOfScripts; then
+    toConsoleError "Cannot determine location of init scripts."
+    finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+}
+
+#== 【4】=【旧配置】读取旧配置参数
+readLegacySetting() {
+  local paramName="${1}"
+  local value
+  value="$(sed -n "s|^${paramName}=||p" "${LEGACY_AGENT_CONF_FILE}" | tr -d '\r' 2>/dev/null)"
+  if [ "${value}" ]; then
+    local valueToPrint="${value}"
+    if [ "${paramName}"x = "${CONF_FIELD_NM_LICENSE}"x ]; then
+      valueToPrint="***"
+    fi
+    toLogInfo "Read legacy value: ${paramName} = ${valueToPrint}"
+  fi
+
+  printf '%s' "${value}"
+}
+
+#== 【4】=【旧配置】迁移旧配置文件
+migrateLegacySettingsFromAgentConf() {
+  toLogInfo "Looking for legacy config to migrate."
+  #== 旧配置文件
+  if [ ! -f "${LEGACY_AGENT_CONF_FILE}" ]; then
+    toLogInfo "Unable to read agent config file, skipping legacy config migration"
+    return
+  fi
+
+  if [ ! "${PARAM_CONFIG_SERVER}" ]; then
+    local configServerValue
+    configServerValue="$(readLegacySetting ${CONF_FIELD_NM_CONFIG_SERVER})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_CONFIG_SERVER}: ${PARAM_CONFIG_SERVER}"
+    if [ "${configServerValue}"x = "http://localhost:8020"x ]; then
+      toLogInfo "Param 'server' has default value set, skipping it"
+    else
+      PARAM_CONFIG_SERVER="${configServerValue}"
+    fi
+  fi
+
+  if [ ! "${PARAM_DATA_SERVER}" ]; then
+    PARAM_DATA_SERVER="$(readLegacySetting ${CONF_FIELD_NM_DATA_SERVER})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_DATA_SERVER}: ${PARAM_DATA_SERVER}"
+  fi
+
+  if [ ! "${PARAM_LICENSE}" ]; then
+    PARAM_LICENSE="$(readLegacySetting ${CONF_FIELD_NM_LICENSE})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_LICENSE}: ${PARAM_LICENSE}"
+  fi
+
+  if [ ! "${PARAM_JSON_CONF}" ]; then
+    PARAM_JSON_CONF="$(readLegacySetting ${CONF_FIELD_NM_JSON_CONF})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_JSON_CONF}: ${PARAM_JSON_CONF}"
+  fi
+
+  if [ ! "${PARAM_USER}" ]; then
+    PARAM_USER="$(readLegacySetting ${CONF_FIELD_NM_USER})"
+    toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_USER}: ${PARAM_USER}"
+
+    if [ ! "${PARAM_GROUP}" ]; then
+      PARAM_GROUP="$(readLegacySetting ${CONF_FIELD_NM_GROUP})"
+      toConsoleInfo "#DEBUG== legacy config: ${CONF_FIELD_NM_GROUP}: ${PARAM_GROUP}"
+    fi
+  fi
+}
+listProcesses() {
+  local errorMessage="${1}"
+  local includeRegex="${2}"
+
+  local excludeRegex=" grep"
+  if [ "${3}" ]; then
+    excludeRegex="grep|${3}"
+  fi
+
+  toLogInfo "#DEBUG== listProcesses command: ps -e -o \"pid,args\" 2>&1 | grep -E \"${includeRegex}\" | awk '{{ print \$1,\$2 }}' | grep -vE \"${excludeRegex}\""
+  local output
+  if ! output="$(pgrep -f "pid,args" 2>&1 | grep -E "${includeRegex}" | awk '{{ print $1,$2 }}')"; then
+    toLogError "Failed to get ${errorMessage}, output: ${output}"
+    return 1
+  fi
+
+  local foundProcesses
+  foundProcesses="$(printf '%s' "${output}" | grep -vE "${excludeRegex}")"
+  if [ ! "${foundProcesses}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${foundProcesses}" | awk '{{ print $1 }}'
+  return 0
+}
+
+#== 版本&帮助信息
+preParams() {
+  if [ $# -gt 0 ]; then
+    if [ "${1}"x = "-h"x ] || [ "${1}"x = "--help"x ]; then
+        displayHelp
+        exit "${EXIT_CODE_OK}"
+    fi
+
+    if [ "${1}"x = "-v"x ] || [ "${1}"x = "--version"x ]; then
+       pad=20
+       printf "%-${pad}s%s\n" "Version:" "${AGENT_INSTALLER_VERSION}"
+       printf "%-${pad}s%s\n" "Commit:" "${AGENT_BUILD_TAG}"
+       printf "%-${pad}s%s\n" "Build At(UTC):" "${AGENT_BUILD_DATE_INFO}"
+       printf "%-${pad}s%s\n" "Uploader:" "${AGENT_BUILD_UPLOADER}"
+       exit "${EXIT_CODE_OK}"
+    fi
+  fi
+}
+#**********************************************************
+# Main script functions
+#**********************************************************
+
+#== 【0】=初始化安装环境
+initializeInstallation() {
+  preParams "$@"
+  #== 设置PATH
+  setPATH
+  #== 配置umask权限
+  local initialUmask
+  initialUmask="$(umask)"
+  umask 022
+
+  #== 读取配置参数
+#  readParamsSection
+  #== 解析命令行参数
+  parseCommandLineParameters "$@"
+  #== 校验配置参数
+
+  #== 是否存在多个同时安装操作【通过判断 INSTALLER_LOCK_FILE 】
+  if isAnotherInstallationRunning; then
+    #== 结束安装操作
+    finishInstallation "${EXIT_CODE_ANOTHER_INSTALLER_RUNNING}" "keep_lock_file"
+  fi
+
+  #== 创建安装标示文件 /tmp/${BRAND_PRODUCT_NAME_LOWER}.lock
+  createInstallationLockFile
+  #== 配置是否更新
+  PARAM_UPGRADE="no"
+
+  #== 获取并校验系统兼容性【LINUX/AIX  X86_64/IA64】
+  local platformDetectionString
+  if ! platformDetectionString="$(checkSystemCompatibility)"; then
+    toConsoleError "${platformDetectionString}"
+    finishInstallation "${EXIT_CODE_OS_NOT_SUPPORTED}"
+  fi
+
+  #== 创建 PARAM_INSTALL_DIR -> INSTALL_DIR 软连接
+  createSymlinkToInstallLocation
+  #== 创建日志目录及日志文件installation_$$.log
+  createLogDirsIfMissing
+  #== 配置信号捕获,执行清空安装临时目录
+  configureSignalHandling "cleanInstallationTemporaryFiles"
+  #== 常规日志
+  initializeLog "${@}"
+
+  toLogInfo "Initial umask: ${initialUmask}"
+  toConsoleInfo "${platformDetectionString}"
+  toLogInfo "Distribution: $(detectLinuxDistribution)"
+}
+
+#== 【3】=安装前检查
+preInstallationChecks() {
+  #== 检查发现初始化系统信息(INIT_SYSTEM、INIT_SYSTEM_VERSION、INIT_DIR)
+  detectInitSystem
+
+  #== 检查目录权限
+  checkAccessRightsToDirs
+  #== 创建临时目录
+  prepareTempFolder
+  #== 检查是否已经安装
+  checkIfAlreadyInstalled
+
+  if [ ! "${PARAM_USER}" ] && [ "${ARCH_ARCH}"x != "AIX"x ] ; then
+    #== 配置文件中配置用户和用户组是否存在
+    checkUserAndGroupFromConfig
+  fi
+
+  #== 检查安装目录空间
+  checkInstallPathFreeSpace
+
+}
+
+uninstallAgent() {
+	toConsoleInfo "Agent already installed. Uninstalling previous version."
+#	printf '%s' "upgrade" >"${UNINSTALL_INFO_FILE_LEGACY_PATH}"
+
+	#shellcheck disable=SC2086
+	"${INSTALL_DIR}/uninstall.sh" $$ ${SKIP_PRIVILEGES_CHECK} 2>>"${LOG_FILE}"
+
+	local uninstallExitCode=$?
+	if [ ${uninstallExitCode} -gt 0 ]; then
+		toConsoleError "Error during uninstalling, code: ${uninstallExitCode}. Installation aborted."
+		finishInstallation "${EXIT_CODE_GENERIC_ERROR}"
+	fi
+
+	toConsoleInfo "Agent uninstalled."
+}
+
+#== 【4】=部署文件
+deployFiles() {
+  if [ "${PARAM_UPGRADE}" = "yes" ]; then
+    uninstallAgent
+  fi
+  #== 【部署】解压文件
+  extractFiles
+  setupAll
+  createCurrentVersionSymlink
+}
+
+#== 【5】=配置安装
+configureInstallation() {
+  #== 配置参数(param、installation.conf)
+  #== 自定义修改配置, sed ,echo ...  参考 applyAgentSettings
+  if [ "${PARAM_TEST}" ]; then
+    toConsoleOnly "Applying test value is: ${PARAM_TEST}"
+  fi
+  #== 获取bool值参数
+  if [ "${PARAM_BOOL}" ]; then
+    toConsoleOnly "Applying bool value is: ${PARAM_BOOL}"
+  fi
+
+
+  if [ "${ARCH_ARCH}"x != "AIX"x ]; then
+    #== 配置用户和用户组
+    if handleUser; then
+      #== 修改文件系统用户和用户组
+      changeFilesOwnership
+    fi
+    #== 设置root权限
+  fi
+  #== 系统配置(策略配置、dump proc、agent 进程配置、自动启动)
+#  setupAutostart
+}
+
+#== 【6】=验证安装状态、运行启动脚本
+postInstallationSteps() {
+  #== 运行agent
+#  runAgents
+  finishInstallation "${EXIT_CODE_OK}"
+}
+
+readonly CURR_PATH="$(pwd)"
+main() {
+  #== 【0】=初始化安装环境
+  initializeInstallation "$@"
+  #== 【3】=安装前检查
+  preInstallationChecks
+  #== 【4】=部署文件
+  deployFiles
+  #== 【5】=配置安装
+  configureInstallation
+  #== 【6】=验证安装状态、运行启动脚本
+  postInstallationSteps
+
+}
+
+#**********************************************************
+# Script start
+#**********************************************************
+main "$@"
+
+################################################
+############# DO NOT REMOVE THIS ###############
+#### DO NOT ADD ANYTHING BELOW THIS COMMENT ####
+################################################
+#################ENDOFSCRIPTMARK################
+
+----SIGNED-INSTALLER

+ 611 - 0
dist/scripts/package.sh

@@ -0,0 +1,611 @@
+#!/bin/bash
+
+AGENT_INSTALLER_VERSION=1.0.0
+
+#== 当前脚本必须放在 smartagent/scripts 目录下
+readonly SCRIPT_PATH=$(dirname $0)
+readonly SCRIPT_DIR=$(cd $SCRIPT_PATH && pwd)
+readonly CURR_FILE_NAME="$(basename "$0")"
+readonly SMARTAGENT_DIR=$(cd $SCRIPT_DIR/../ && pwd)
+readonly SMARTAGENT_PARENT_DIR=$(cd $SCRIPT_DIR/../../ && pwd)
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+BRAND_PRODUCT_NAME="euspace"
+BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME}-${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="cwserveragent"
+readonly BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME="ucloudwise"
+
+
+readonly BAK_EXT_NAME=".bak"
+#== cw-serveragent
+readonly SERVICE_SCRIPT_FILE="cw-oneagent"
+readonly XZDEC_NAME="xzdec"
+readonly INSTALL_TEMP_FILE_NAME="install_temp.sh"
+readonly INSTALL_VERSION_FILE_NAME="installer.version"
+#== {productName}-installer-{version}.sh 即 cwserveragent-installer-1.1.0.sh
+ARCH=$(uname -m)
+TARGET_INSTALL_FILE_NAME="${BRAND_AGENT_PRODUCT_NAME}-installer-Linux-${ARCH}-${AGENT_INSTALLER_VERSION}.sh"
+#TARGET_INSTALL_FILE_NAME="${BRAND_PRODUCT_NAME_LOWER}-installer-${AGENT_INSTALLER_VERSION}.sh"
+
+TARGET_INSTALL_TMP_FILE="${SMARTAGENT_PARENT_DIR}/${TARGET_INSTALL_FILE_NAME}.tmp"
+
+readonly LOG_DIR="${SMARTAGENT_PARENT_DIR}/logs"
+readonly LOG_FILE_NAME="release.log"
+readonly LOG_FILE="${LOG_DIR}/${LOG_FILE_NAME}"
+
+#== 【0】=【退出码】执行成功
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+readonly EXIT_CODE_NOT_ENOUGH_SPACE=6
+readonly EXIT_CODE_NOT_ENOUGH_MEMORY=7
+#== 【0】=无效参数
+readonly EXIT_CODE_INVALID_PARAM=8
+readonly EXIT_CODE_INSUFFICIENT_PERMISSIONS=9
+readonly EXIT_CODE_SEMANAGE_NOT_FOUND=10
+readonly EXIT_CODE_WATCHDOG_ALREADY_RUNNING=11
+#== 【退出码】接收信号
+readonly EXIT_CODE_SIGNAL_RECEIVED=12
+readonly EXIT_CODE_ANOTHER_INSTALLER_RUNNING=13
+readonly EXIT_CODE_AGENT_CONTAINER_RUNNING=14
+readonly EXIT_CODE_GLIBC_VERSION_TOO_LOW=15
+readonly EXIT_CODE_CORRUPTED_PACKAGE=16
+#== 【退出码】管理系统环境变量配置
+readonly EXIT_CODE_MISCONFIGURED_ENVIRONMENT=17
+readonly EXIT_CODE_UNSUPPORTED_DOWNGRADE=18
+readonly EXIT_CODE_OS_NOT_SUPPORTED=19
+readonly EXIT_CODE_DEL_DEFAULT_PARAMS=20
+
+readonly LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS=50
+#**********************************************************
+# Logs
+#**********************************************************
+toLogFile() {
+  if [ -e "${LOG_FILE}" ]; then
+    printf '%s UTC %s\n' "$(date -u +"%Y-%m-%d %H:%M:%S")" "$*" >>"${LOG_FILE}" 2>/dev/null
+  fi
+}
+
+toConsoleOnly() {
+  printf '%s %s\n' "$(date +"%H:%M:%S")" "$*" 2>/dev/null
+}
+
+toLogInfo() {
+  toLogFile "[INFO]" "$@"
+}
+
+toLogWarn() {
+  toLogFile "[WARN]" "$@"
+}
+
+toLogError() {
+  toLogFile "[ERROR]" "$@"
+}
+
+toLogAdaptive() {
+  local success="${1}"
+  shift
+  if [ "${success}" -eq 0 ]; then
+    toLogInfo "$@"
+  else
+    toLogError "$@"
+  fi
+}
+
+toConsoleInfo() {
+  toConsoleOnly "$@"
+  toLogInfo "$@"
+}
+
+toConsoleWarn() {
+  toConsoleOnly "Warn:" "$@"
+  toLogWarn "$@"
+} >&2
+
+toConsoleError() {
+  toConsoleOnly "Error:" "$@"
+  toLogError "$@"
+} >&2
+
+#**********************************************************
+# compress
+#**********************************************************
+tarSmartAgent() {
+  toConsoleInfo "There are 5 steps to packing, begin package ${BRAND_AGENT_PRODUCT_NAME}...."
+  #== 将打包脚本(package.sh、xzdec、install_temp.sh、smartagent、uninstall.sh)移动到临时目录
+  for fileNm in "${CURR_FILE_NAME}" "${INSTALL_TEMP_FILE_NAME}" "uninstall.sh"; do
+    mv -f "${SCRIPT_DIR}/${fileNm}" "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" 2>>"${LOG_FILE}"
+  done
+  mv -f "${SCRIPT_DIR}/${XZDEC_NAME}" "${SMARTAGENT_PARENT_DIR}/" 2>>"${LOG_FILE}"
+  cp -f "${SMARTAGENT_PARENT_DIR}/${INSTALL_TEMP_FILE_NAME}${BAK_EXT_NAME}" "${TARGET_INSTALL_TMP_FILE}" 2>>"${LOG_FILE}"
+
+  for fileNm in "uninstall.sh"; do
+    cp -f "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" "${SCRIPT_DIR}/${fileNm}" 2>>"${LOG_FILE}"
+  done
+
+  #== 删除安装脚本中注释
+  if [ "${PARAM_DEBUG}"x != "true"x ]; then
+    toConsoleInfo "Delete the type [#==] comment ..."
+    sed -i '/#==/d' "${TARGET_INSTALL_TMP_FILE}"
+#    sed -i '/#==/d' "${SCRIPT_DIR}/${SERVICE_SCRIPT_FILE}"
+    sed -i '/#==/d' "${SCRIPT_DIR}/uninstall.sh"
+    toConsoleInfo "Delete the type [#DEBUG==] comment ..."
+    sed -i '/#DEBUG==/d' "${TARGET_INSTALL_TMP_FILE}"
+#    sed -i '/#DEBUG==/d' "${SCRIPT_DIR}/${SERVICE_SCRIPT_FILE}"
+    sed -i '/#DEBUG==/d' "${SCRIPT_DIR}/uninstall.sh"
+  fi
+
+  if [ "${PARAM_RELEASE}"x = "true"x ]; then
+    deleteDefaultParams "${TARGET_INSTALL_TMP_FILE}"
+  fi
+
+  #== 进入 smartagent 目录下
+  cd "${SMARTAGENT_DIR}"
+  cp -f "${SCRIPT_DIR}/uninstall.sh" ./package_dir
+  #== 执行第一阶段压缩(源码压缩)Cloudwise-SmartAgent.tar
+  toConsoleInfo "step【1】start【tar -cpf "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar" ./package_dir/*】to ${BRAND_AGENT_PRODUCT_NAME}.tar"
+  tar -cpf "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar" ./package_dir/* >>"${LOG_FILE}" 2>&1
+  rm -f "./package_dir/uninstall.sh"
+
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+  local agentTarSize="$(getStorageSize "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar")"
+
+  cd "${SMARTAGENT_PARENT_DIR}"
+  #== 执行第二阶段压缩(源码压缩 Cloudwise-SmartAgent.tar.xz
+  toConsoleInfo "step【2】start【xz -T0 -z ${BRAND_AGENT_PRODUCT_NAME}.tar】to ${BRAND_AGENT_PRODUCT_NAME}.tar.zx"
+  xz -T0 -1 -z "${BRAND_AGENT_PRODUCT_NAME}.tar" -vv
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 执行第三阶段压缩 Cloudwise-SmartAgent.tar.xz xzdec to Cloudwise-SmartAgent.tar
+  toConsoleInfo "step【3】start【tar -cpf ${BRAND_AGENT_PRODUCT_NAME}.tar ${BRAND_AGENT_PRODUCT_NAME}.tar.xz xzdec】 to ${BRAND_AGENT_PRODUCT_NAME}.tar"
+  tar -cpf "${BRAND_AGENT_PRODUCT_NAME}.tar" "${BRAND_AGENT_PRODUCT_NAME}.tar.xz" xzdec >>"${LOG_FILE}" 2>&1
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 执行第四阶段压缩 Cloudwise-SmartAgent.tar to Cloudwise-SmartAgent.tar.base64
+  toConsoleInfo "step【4】start【base64 ${BRAND_AGENT_PRODUCT_NAME}.tar > ${BRAND_AGENT_PRODUCT_NAME}.tar.base64】 to ${BRAND_AGENT_PRODUCT_NAME}.tar.base64"
+  base64 "${BRAND_AGENT_PRODUCT_NAME}.tar" >"${BRAND_AGENT_PRODUCT_NAME}.tar.base64" 2>>"${LOG_FILE}"
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  #== 执行第五阶段将 base64 文件写入安装脚本
+  toConsoleInfo "step【5】start write 【${BRAND_AGENT_PRODUCT_NAME}.tar.base64】 to ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}"
+  #== 设置脚本中版本、创建时间、占用空间信息
+  editInstallScriptParam "${TARGET_INSTALL_TMP_FILE}" ${agentTarSize}
+
+  sed -i "/#################ENDOFSCRIPTMARK################/r ${BRAND_AGENT_PRODUCT_NAME}.tar.base64" "${TARGET_INSTALL_TMP_FILE}"
+  if [ $? -ne 0 ]; then
+    finishPackage "${EXIT_CODE_GENERIC_ERROR}"
+  fi
+
+  toConsoleInfo "${BRAND_AGENT_PRODUCT_NAME} packaging success."
+  finishPackage "${EXIT_CODE_OK}"
+}
+
+buildVersion() {
+  local oldVersion
+  if oldVersion="$(getOldVersion)"; then
+    setInstallVersion "${oldVersion}"
+    removeIfExist "${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}"
+		toConsoleInfo "Delete file ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}."
+  fi
+
+  if [ "${PARAM_VERSION}" ]; then
+    setInstallVersion "${PARAM_VERSION}"
+    return
+  fi
+}
+
+setInstallVersion() {
+  local version="${1}"
+  if [ ! "${version}" ]; then
+    toConsoleWarn "Set install version failed,not found version version."
+    return
+  fi
+
+  AGENT_INSTALLER_VERSION="${version}"
+  BRAND_PRODUCT_NAME="${PARAM_NAME}"
+  BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME}-${BRAND_PRODUCT_NAME}"
+  TARGET_INSTALL_FILE_NAME="${BRAND_AGENT_PRODUCT_NAME}-installer-Linux-${ARCH}-${AGENT_INSTALLER_VERSION}.sh"
+  #== {productName}-installer-{version}.sh 即 cwserveragent-installer-1.1.0.sh
+#  TARGET_INSTALL_FILE_NAME="${BRAND_PRODUCT_NAME_LOWER}-installer-${AGENT_INSTALLER_VERSION}.sh"
+  TARGET_INSTALL_TMP_FILE="${SMARTAGENT_PARENT_DIR}/${TARGET_INSTALL_FILE_NAME}.tmp"
+}
+
+getOldVersion() {
+	local installVersionFile="${SMARTAGENT_DIR}/installer.version"
+	if [ ! -f "${installVersionFile}" ]; then
+		toLogWarn "Could not find ${installVersionFile} file."
+		return 1
+	fi
+
+	local oldVersion="$(cat "${installVersionFile}")"
+	if [ ! "${oldVersion}" ]; then
+		toLogWarn "Could not read install version from ${installVersionFile} file."
+	  return 1
+  fi
+
+  printf '%s' "${oldVersion}"
+  return 0
+}
+
+#== 通过占位分割读取位置
+locateDelimiter() {
+  #== 占位符
+  local delimiter="${1}"
+  #== 从文件结尾读取行数
+  local linesToReadFromEnd="${2}"
+  local filePath="${3}"
+
+  if [ "${linesToReadFromEnd}" ]; then
+    #== 文件总行数n(实际行数=n+1)
+    local linesCount="$(wc -l "${filePath}" | awk '{print $1}')"
+    #== 从后往前读取【linesToReadFromEnd】行
+    local offset="$(tail -n"${linesToReadFromEnd}" "${filePath}" 2>/dev/null | awk '/^'"${delimiter}"'/ { print NR; exit }')"
+    if [ -n "${offset}" ]; then
+      printf "%d" "$((linesCount - linesToReadFromEnd + offset))"
+    fi
+  else
+    #== 读取占位符所在行(实际行数=n+1)
+    awk '/^'"${delimiter}"'/ { print NR; exit }' "${filePath}"
+  fi
+}
+
+#== 删除安装脚本中默认参数
+deleteDefaultParams() {
+  local filePath="${1}"
+  local sectionName="----PARAMETERS"
+  local begin=$(locateDelimiter "${sectionName}" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS} ${filePath})
+  local end=$(locateDelimiter "${sectionName}--" ${LINES_TO_SEARCH_FOR_SIGNATURE_AND_PARAMS} ${filePath})
+
+  if [ -z "${begin}" ] || [ -z "${end}" ]; then
+    return
+  fi
+
+  toConsoleInfo "Begin delete default params from line ${begin} to ${end}..."
+  local output="$(sed -i "${begin},${end}d" "${filePath}") 2>&1"
+  if [ ! "${output}" ]; then
+    toConsoleError "Delete install script file ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME} default params error:${output}"
+    finishPackage "${EXIT_CODE_DEL_DEFAULT_PARAMS}"
+  fi
+  toConsoleInfo "Delete install script file ${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME} default params success."
+}
+
+#== 设置脚本中版本、创建时间、占用空间信息
+editInstallScriptParam() {
+  local targetFile="${1}"
+  local agentTarSize="${2}"
+  #== 设置版本信息
+  local buildDate="$(date +"%Y-%m-%d")"
+  local buildTag="$(git rev-parse --short HEAD)"
+  local buildDateInfo="$(date +"%Y-%m-%d %H:%M:%S")"
+  local buildUploader="$(hostname)/${USER}"
+  #== 构建日期
+  editScriptFileParam "readonly AGENT_BUILD_DATE" "${buildDate}" "${targetFile}"
+  #== 构建版本
+  editScriptFileParam "readonly AGENT_INSTALLER_VERSION" "${AGENT_INSTALLER_VERSION}" "${targetFile}"
+  #== commitid
+  editScriptFileParam "readonly AGENT_BUILD_TAG" "'${buildTag}'" "${targetFile}"
+  #== 构建日期
+  editScriptFileParam "readonly AGENT_BUILD_DATE_INFO" "'${buildDateInfo}'" "${targetFile}"
+  #== 构建环境信息
+  editScriptFileParam "readonly AGENT_BUILD_UPLOADER" "'${buildUploader}'" "${targetFile}"
+
+  #== 设置占用空间信息
+  local agentDirSize="$(getStorageSize "${SMARTAGENT_DIR}")"
+  local agentBase64FileSize="$(getStorageSize "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.tar.base64")"
+  local agentInstallFileSize="$(getStorageSize "${TARGET_INSTALL_TMP_FILE}")"
+  local artifactsSize="$((agentBase64FileSize + agentInstallFileSize + agentDirSize))";
+  editScriptFileParam "readonly EXTERNAL_TAR_SIZE" "${agentTarSize}" "${targetFile}"
+  editScriptFileParam "readonly ARTIFACTS_SIZE" "${artifactsSize}" "${targetFile}"
+}
+
+getStorageSize() {
+  local dirName="${1}"
+  if [ ! -e "${dirName}" ]; then
+    printf '%s' "0"
+    return 0
+  fi
+  printf '%s' "$(du -sb "${dirName}" | awk '{print $1}')"
+}
+
+finishFinally() {
+  #== 将打包脚本文件移动到当前目录下
+  for fileNm in "${CURR_FILE_NAME}" "${INSTALL_TEMP_FILE_NAME}" "uninstall.sh"; do
+    if [ -f "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" ]; then
+      mv -f "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}" "${SCRIPT_DIR}/${fileNm}" 2>>"${LOG_FILE}"
+    fi
+  done
+  removeIfExist "${SMARTAGENT_DIR}/uninstall.sh"
+  if [ -f "${SMARTAGENT_PARENT_DIR}/${XZDEC_NAME}" ]; then
+    mv -f "${SMARTAGENT_PARENT_DIR}/${XZDEC_NAME}" "${SCRIPT_DIR}/" 2>>"${LOG_FILE}"
+  fi
+
+  if [ -f "${TARGET_INSTALL_TMP_FILE}" ]; then
+    mv -f "${TARGET_INSTALL_TMP_FILE}" "${SMARTAGENT_DIR}/${TARGET_INSTALL_FILE_NAME}" 2>>"${LOG_FILE}"
+  fi
+
+  if [ "${PARAM_DEBUG}"x != "true"x ]; then
+    for fileNmExt in "tar" "tar.xz" "tar.base64"; do
+      removeIfExist "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.${fileNmExt}"
+    done
+  fi
+
+
+}
+
+removeIfExist() {
+  local filePath="${1}"
+  if [ ! -e "${filePath}" ]; then
+    return
+  fi
+  rm -rf "${filePath}" 2>>"${LOG_FILE}"
+}
+
+deleteParentTmpFile() {
+  for fileNm in "${CURR_FILE_NAME}" "${INSTALL_TEMP_FILE_NAME}" "uninstall.sh"; do
+    removeIfExist "${SMARTAGENT_PARENT_DIR}/${fileNm}${BAK_EXT_NAME}"
+  done
+  removeIfExist "${SMARTAGENT_PARENT_DIR}/${XZDEC_NAME}"
+  removeIfExist "${TARGET_INSTALL_TMP_FILE}"
+
+  for fileNmExt in "tar" "tar.xz" "tar.base64"; do
+    removeIfExist "${SMARTAGENT_PARENT_DIR}/${BRAND_AGENT_PRODUCT_NAME}.${fileNmExt}"
+  done
+}
+
+#**********************************************************
+# Param
+#**********************************************************
+PARAM_DEBUG=false
+PARAM_RELEASE=false
+#PARAM_VERSION=1.1.0
+PARAM_NAME=chaosd
+PARAM_AGENT_PROC=$PARAM_NAME
+PARAM_CONFIG_PATH=
+
+#== 大小写转化,并返回判断结果
+istrcmp() {
+  local s1="$(printf '%s' "${1}" | tr '[:upper:]' '[:lower:]')"
+  local s2="$(printf '%s' "${2}" | tr '[:upper:]' '[:lower:]')"
+  [ "${s1}"x = "${s2}"x ]
+}
+
+isParamTrue() {
+  [ "${1}"x = "1"x ] || [ "${1}"x = "true"x ] || [ "${1}"x = "enable"x ] || [ "${1}"x = "yes"x ]
+}
+
+isParamFalse() {
+  [ "${1}"x = "0"x ] || [ "${1}"x = "false"x ] || [ "${1}"x = "disable"x ] || [ "${1}"x = "no"x ]
+}
+
+#== 获取参数值
+getParamValue() {
+  local paramName="${1}"
+  local input="${2}"
+  local paramNameLength="${#paramName}"
+  paramNameLength=$((paramNameLength + 1))
+
+  local partParam="$(expr substr "${input}" 1 ${paramNameLength})"
+  if ! istrcmp "${partParam}" "${paramName}="; then
+    return 1
+  fi
+
+  local valueSeparator=$((paramNameLength + 1))
+  local value="$(expr substr "${input}" ${valueSeparator} 1000)"
+  if [ -z "${value}" ]; then
+    return 1
+  fi
+
+  printf '%s' "${value}"
+  return 0
+}
+
+#== 获取bool参数
+readBoolParam() {
+  local value=
+  if value="$(getParamValue "${1}" "${2}")"; then
+    if isParamFalse "${value}"; then
+      printf "false"
+      return 0
+    fi
+    if isParamTrue "${value}"; then
+      printf "true"
+      return 0
+    fi
+  fi
+
+  return 1
+}
+
+#== 解析命令行中参数
+parseCommandLineParameters() {
+  while [ $# -gt 0 ]; do
+    local param="${1}"
+    local value=
+
+    if value=$(getParamValue RELEASE "${param}"); then
+      PARAM_RELEASE="${value}"
+      printParamMessage "RELEASE" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue VERSION "${param}"); then
+      PARAM_VERSION="${value}"
+      printParamMessage "VERSION" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue NAME "${param}"); then
+      PARAM_NAME="${value}"
+      printParamMessage "NAME" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue CONFIG "${param}"); then
+      PARAM_CONFIG_PATH="${value}"
+      printParamMessage "CONFIG" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(getParamValue PROC "${param}"); then
+      PARAM_AGENT_PROC="${value}"
+      printParamMessage "PROC" "${value}"
+      shift
+      continue
+    fi
+
+    if value=$(readBoolParam DEBUG "${param}"); then
+      PARAM_DEBUG="${value}"
+      printParamMessage "DEBUG" "${value}"
+      shift
+      continue
+    fi
+
+    if [ "${param}"x = "-h"x ] || [ "${param}"x = "--help"x ]; then
+      displayHelp
+      finishPackage "${EXIT_CODE_OK}"
+    fi
+
+    if [ "${param}"x = "-v"x ] || [ "${param}"x = "--version"x ]; then
+      printf "%s\n" "${AGENT_INSTALLER_VERSION}"
+      finishPackage "${EXIT_CODE_OK}"
+    fi
+
+    toConsoleError "Unrecognized parameter: ${param}"
+
+    displayHelp
+    finishPackage "${EXIT_CODE_INVALID_PARAM}"
+  done
+}
+
+displayHelp() {
+  printf '\n'
+  printf '%s\n' "Usage: "${CURR_FILE_NAME}" [-h] [-v] [NAME=pluginName] [PROC=mainProcess] [RELEASE=true|false] [DEBUG=true|false] [VERSION=1.0]"
+  printf '\n\n'
+
+  local pad=15
+  printf "%-${pad}s%s\n" "-h, --help" "Display this help and exit."
+  printf "%-${pad}s%s\n" "-v, --version" "Print version and exit."
+  printf '\n'
+  printf "%-${pad}s%s\n" "RELEASE" "Default true; Whether to delete the publish parameter in the script."
+  printf "%-${pad}s%s\n" "VERSION" "Configure the ${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME} version."
+  printf "%-${pad}s%s\n" "NAME" "Setting agent name like 'bash ./scripts/package.sh NAME=pluginName PROC=mainProcess'"
+  printf "%-${pad}s%s\n" "PROC" "Setting agent main process name."
+  printf "%-${pad}s%s\n" "DEBUG" "Default false; 1、Debug mode executed script ${CURR_FILE_NAME};"
+  printf "%-${pad}s%s\n" "" "2、Remove the debug log from the scripts (${TARGET_INSTALL_FILE_NAME}、${SERVICE_SCRIPT_FILE}、uninstall.sh)."
+}
+
+#== 完成安装后清理临时目录
+finishPackage() {
+  finishFinally
+  toLogInfo "Installation finished, PID $$, exit code: ${1}."
+  exit "${1}"
+}
+
+printParamMessage() {
+  local paramNm="${1}"
+  local paramValue="${2}"
+  toConsoleInfo "---> Parameter ${paramNm}=${paramValue}."
+}
+
+#**********************************************************
+# SELinux related functions
+#**********************************************************
+#== 修改scripts/smartagent脚本配置信息
+editScriptFileParam() {
+  local key="${1}"
+  local newValue="${2}"
+  local file="${3}"
+
+  local value="$(sed -n "s|^${key}=||p" "${file}" 2>/dev/null)"
+  toConsoleInfo "---> edit ${key}=${value} to ${key}=${newValue}, configFile: ${file}"
+  if [ "${value}" ]; then
+    sed -i "s|^${key}=.*|${key}=${newValue}|" "${file}" 2>/dev/null
+  fi
+}
+
+#== 信号捕获,并执行回调函数
+signalHandler() {
+  local signal="${1}"
+  local callback="${2}"
+  toLogWarn "process received signal: ${signal}"
+  ${callback} "${signal}"
+  exit ${EXIT_CODE_SIGNAL_RECEIVED}
+}
+
+#== 配置信号捕获和回调
+configureSignalHandling() {
+  local callback="${1}"
+  for signal in HUP INT QUIT ABRT ALRM TERM; do
+    #== shellcheck disable=SC2064
+    trap "signalHandler ${signal} ${callback}" ${signal}
+  done
+
+  trap "" PIPE
+}
+
+#**********************************************************
+# main
+#**********************************************************
+main() {
+  mkdir -p "${LOG_DIR}"
+  echo "" >"${LOG_FILE}"
+
+  toConsoleInfo "package script run path: ${SCRIPT_DIR}"
+  toConsoleInfo "package logs path: ${LOG_DIR}"
+
+  chmod +x ${SCRIPT_DIR}/*
+
+  #== 解析命令行参数
+  parseCommandLineParameters "$@"
+  toConsoleInfo "-----params PARAM_RELEASE: ${PARAM_RELEASE}, PARAM_DEBUG: ${PARAM_DEBUG}, PARAM_VERSION: ${PARAM_VERSION}"
+
+  configureSignalHandling "finishFinally"
+
+  #== 设置 package 插件名
+  editScriptFileParam "BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/package.sh"
+  #== 设置 install 插件名
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/install_temp.sh"
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME_LOWER" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/install_temp.sh"
+  #== 设置 agentctl 插件名(存在时)
+  if [ -f "${SMARTAGENT_DIR}/package_dir/bin/agentctl" ];then
+    editScriptFileParam "readonly BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+    editScriptFileParam "readonly BRAND_PRODUCT_NAME_LOWER" "\"${PARAM_NAME}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+  #== 设置 agentctl 配置文件路径(存在时)
+    editScriptFileParam "readonly AGENT_CONFIG_PATH" "\"${PARAM_CONFIG_PATH}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+  fi
+
+  #== 设置 uninstall 插件名
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/uninstall.sh"
+  editScriptFileParam "readonly BRAND_PRODUCT_NAME_LOWER" "\"${PARAM_NAME}\"" "${SCRIPT_DIR}/uninstall.sh"
+  #== 设置主进程名
+  if [ -n "${PARAM_AGENT_PROC}" ]; then
+    editScriptFileParam "readonly AGENT_PROC" "\"${PARAM_AGENT_PROC}\"" "${SCRIPT_DIR}/install_temp.sh"
+    editScriptFileParam "readonly AGENT_PROC" "\"${PARAM_AGENT_PROC}\"" "${SCRIPT_DIR}/uninstall.sh"
+    editScriptFileParam "readonly AGENT_PROC" "\"${PARAM_AGENT_PROC}\"" "${SMARTAGENT_DIR}/package_dir/bin/agentctl"
+  fi
+  buildVersion
+  toConsoleInfo "-----Write release version ${AGENT_INSTALLER_VERSION} to ${SMARTAGENT_DIR}/${INSTALL_VERSION_FILE_NAME} file."
+  echo "${AGENT_INSTALLER_VERSION}">"${SMARTAGENT_DIR}/${INSTALL_VERSION_FILE_NAME}"
+
+  #== 修改uninstall.sh 版本
+  editScriptFileParam "readonly AGENT_INSTALLER_VERSION" "${AGENT_INSTALLER_VERSION}" "${SCRIPT_DIR}/uninstall.sh"
+
+  deleteParentTmpFile
+
+  #== 压缩 并生成安装脚本
+  tarSmartAgent
+}
+
+main "$@"

+ 1111 - 0
dist/scripts/uninstall.sh

@@ -0,0 +1,1111 @@
+#!/bin/bash
+
+#==脚本执行中遇到不存在的变量就报错
+set -o nounset
+#==脚本执行中报错即刻退出
+set -o errexit
+#==脚本执行只要一个子命令失
+set -o pipefail
+
+readonly BRAND_FORMAL_NAME="Cloudwise"
+readonly BRAND_PRODUCT_NAME="euspace"
+#== 根路径
+readonly BRAND_PARENT_PRODUCT_NAME="omniagent"
+readonly BRAND_AGENT_PRODUCT_NAME="${BRAND_FORMAL_NAME} ${BRAND_PRODUCT_NAME}"
+readonly BRAND_FORMAL_NAME_LOWER="cloudwise"
+readonly BRAND_PRODUCT_NAME_LOWER="euspace"
+readonly BRAND_AGENT_DEFAULT_USER_AND_GROUP_NAME="cloudwise"
+
+#== **********************************************************
+#==  配置重要文件名称
+#== **********************************************************
+#== smartagentd
+readonly AGENT_PROC="euspace"
+readonly AGENT_INSTALLER_VERSION=0.0.2
+#== **********************************************************
+#==  配置目录
+#== **********************************************************
+readonly INSTALL_BASE=/opt
+#== /opt/cloudwise/omniagent/agents 目录
+readonly BASE_INSTALL_DIR=${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents
+#== cloudwise/omniagent/agents/agent
+readonly INSTALL_FOLDER=${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/agents/${BRAND_PRODUCT_NAME_LOWER}
+#== cloudwise/omniagent/agents/agent/version
+readonly INSTALL_VERSION_FOLDER=${INSTALL_FOLDER}/${AGENT_INSTALLER_VERSION}
+#== /opt/cloudwise/omniagent/agents/agent/version
+readonly INSTALL_DIR=${INSTALL_BASE}/${INSTALL_VERSION_FOLDER}
+#== /opt/cloudwise/omniagent/agents/agent
+readonly AGENT_BASE_DIR=${INSTALL_BASE}/${INSTALL_FOLDER}
+
+readonly CURRENT_VERSION_LINK=${AGENT_BASE_DIR}/current
+
+#== /opt/cloudwise/omniagent/conf
+readonly AGENT_CONF_DIR="${INSTALL_DIR}/conf"
+
+readonly AGENT_LIBS_DIR="${INSTALL_DIR}/libs"
+
+#== agent 初始化脚本目录 /opt/cloudwise/omniagent/scripts
+readonly AGENT_SCRIPTS_DIR="${INSTALL_DIR}/scripts"
+
+#== agent 初始化脚本目录 /opt/cloudwise/omniagent/bin
+readonly AGENT_BIN_DIR="${INSTALL_DIR}/bin"
+
+
+readonly PERL_FILE="/usr/bin/perl"
+
+readonly SAFE_RM_FILE="safe-rm"
+
+readonly SAFE_RM_BIN="${AGENT_BIN_DIR}/${SAFE_RM_FILE}"
+
+if [ -f ${PERL_FILE} ] && [ -f "${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/bin/${SAFE_RM_FILE}" ]; then
+    readonly SAFE_RM_EXEC="${INSTALL_BASE}/${BRAND_FORMAL_NAME_LOWER}/${BRAND_PARENT_PRODUCT_NAME}/bin/${SAFE_RM_FILE}"
+else
+    readonly SAFE_RM_EXEC="rm"
+fi
+
+readonly LOG_DIR_NAME=logs
+#== /opt/cloudwise/omniagent/logs
+readonly LOG_DIR=${INSTALL_DIR}/${LOG_DIR_NAME}
+
+#== /var/log/cloudwise/omniagent/
+readonly INSTALLER_LOG_DIR="/var/log/${INSTALL_FOLDER}"
+
+#readonly INSTALLER_LOG_SUBDIR="installer"
+##== /opt/cloudwise/smartagent/log/installer
+#readonly INSTALLER_LOG_DIR=${LOG_DIR}/${INSTALLER_LOG_SUBDIR}
+
+readonly AGENT_PID_FILE="${INSTALL_DIR}/bin/${AGENT_PROC}.pid"
+
+
+#== 【0】=
+readonly INSTALLER_LOCK_FILE="/tmp/${BRAND_FORMAL_NAME_LOWER}_${BRAND_PRODUCT_NAME_LOWER}.lock"
+
+readonly INIT_SYSTEM_SYSV="SysV"
+#== 系统 systemd
+readonly INIT_SYSTEM_SYSTEMD="systemd"
+#== cw-serveragent
+readonly SERVICE_SCRIPT_FILE="cw-oneagent"
+#== cw-serveragent.service
+readonly SYSTEMD_UNIT_FILE_AGENT="${SERVICE_SCRIPT_FILE}.service"
+
+#== 系统文件目录
+readonly SYSTEMD_UNIT_FILES_DIR="/etc/systemd/system/"
+
+readonly EXIT_CODE_OK=0
+readonly EXIT_CODE_GENERIC_ERROR=1
+
+readonly EXIT_CODE_SIGNAL_RECEIVED=12
+readonly EXIT_CODE_ANOTHER_INSTALLER_RUNNING=13
+readonly EXIT_CODE_AGENT_CONTAINER_RUNNING=14
+readonly EXIT_CODE_GLIBC_VERSION_TOO_LOW=15
+readonly EXIT_CODE_MISCONFIGURED_ENVIRONMENT=17
+
+
+readonly USER="root"
+readonly GROUP="root"
+#== 非root模式
+readonly PARAM_NON_ROOT_MODE=false
+
+readonly CONTAINER_DEPLOYMENT_STATE_ENTRY=false
+#== 是否有 SELinux
+readonly ARCH_HAS_SELINUX=true
+################################################################################
+#	Logs
+################################################################################
+toLog() {
+  if [ -e "${LOG_FILE}" ]; then
+    printf '%s UTC %s\n' "$(date -u +"%Y-%m-%d %H:%M:%S")" "$*" >>"${LOG_FILE}" 2>/dev/null
+  fi
+}
+
+toConsoleOnly() {
+  printf '%s %s\n' "$(date +"%H:%M:%S")" "$*" 2>/dev/null
+}
+
+toLogInfo() {
+  toLog "[INFO]" "$@"
+}
+
+toLogWarning() {
+  toLog "[WARN]" "$@"
+}
+
+toLogError() {
+  toLog "[ERROR]" "$@"
+}
+
+toLogAdaptive() {
+  local success="${1}"
+  shift
+  if [ "${success}" -eq 0 ]; then
+    toLogInfo "$@"
+  else
+    toLogError "$@"
+  fi
+}
+
+toConsoleInfo() {
+  toConsoleOnly "$@"
+  toLogInfo "$@"
+}
+
+toConsoleWarning() {
+  toConsoleOnly "Warning:" "$@"
+  toLogWarning "$@"
+} >&2
+
+toConsoleError() {
+  toConsoleOnly "Error:" "$@"
+  toLogError "$@"
+} >&2
+
+createDirIfNotExistAndSetRights() {
+  local dir="${1}"
+  local rights="${2}"
+
+  if [ ! -d "${dir}" ]; then
+    toLogInfo "Creating directory ${dir} with rights ${rights}"
+    local message
+    if ! message="$(mkdir -p "${dir}" 2>&1)"; then
+      toConsoleWarning "Cannot create ${dir} directory."
+      toLogWarning "Error message: ${message}"
+      return 1
+    fi
+  fi
+
+  if ! message="$(chmod "${rights}" "${dir}" 2>&1)"; then
+    toConsoleWarning "Cannot change permisions of ${dir} directory to ${rights}."
+    toLogWarning "Error message: ${message}"
+    return 1
+  fi
+
+  return 0
+}
+
+#== 创建日志目录及日志文件installation_$$.log
+createLogDirsIfMissing() {
+  createDirIfNotExistAndSetRights "${INSTALL_BASE}" u+rwx,g+rx,o+rx
+  createDirIfNotExistAndSetRights "${BASE_INSTALL_DIR}" u+rwx,g+rx,o+rx
+  createDirIfNotExistAndSetRights "${INSTALL_DIR}" 1775
+  createDirIfNotExistAndSetRights "${LOG_DIR}" 1777
+  createDirIfNotExistAndSetRights "${INSTALLER_LOG_DIR}" 1777
+
+#  for agentLog in ${PLUGIN_LOG_NAMES}; do
+#    createDirIfNotExistAndSetRights "${LOG_DIR}/${agentLog}" 1777
+#  done
+
+  touch "${LOG_FILE}"
+  setRightsForFiles "${LOG_FILE}" 766
+}
+
+
+################################################################################
+#	Platform characteristics detection
+################################################################################
+
+#== 设置文件可执行权限
+setRightsForFiles() {
+  local file="${1}"
+  local perms="${2}"
+
+  if [ -f "${file}" ]; then
+    chmod "${perms}" "${file}"
+  elif [ -d "${file}" ]; then
+    chmod -R "${perms}" "${file}"
+  fi
+}
+
+#== 安装类型(f:文件,d:目录)递归设置权限
+chmod4FilesRecursively() {
+  local dir="${1}"
+  local type="${2}"
+  local mask="${3}"
+  find "${dir}" -type "${type}" -exec chmod "${mask}" {} \;
+}
+
+detectLinuxDistribution() {
+  if [ -f /etc/redhat-release ]; then
+    cat /etc/redhat-release
+  elif [ -f /etc/os-release ]; then
+    (
+      . /etc/os-release
+      local distrib="${NAME}"
+      if [ -z "${distrib}" ]; then
+        distrib="${ID}"
+      fi
+
+      local version="${VERSION_ID}"
+      if printf "%s" "${distrib}" | grep -iq "debian"; then
+        version="$(cat /etc/debian_version)"
+      fi
+
+      printf "%s %s" "${distrib}" "${version}"
+    )
+  elif [ -f /etc/SuSE-release ]; then
+    head -1 /etc/SuSE-release
+  elif [ -f /etc/lsb-release ]; then
+    (
+      . /etc/lsb-release
+      printf "%s %s" "${DISTRIB_ID}" "${DISTRIB_RELEASE}"
+    )
+  elif ls /etc/*release* >/dev/null 2>&1; then
+    # Generic fallback
+    cat /etc/*release*
+  else
+    printf "AIX %s" "$(oslevel -s 2>&1)"
+  fi
+}
+
+checkInitSystem() {
+  local version
+  if version="$(systemctl --version 2>&1)"; then
+    if [ -d "${SYSTEMD_UNIT_FILES_DIR}" ]; then
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSTEMD}
+    else
+      readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+      toLogWarning "${INIT_SYSTEM_SYSTEMD} was detected but ${SYSTEMD_UNIT_FILES_DIR} does not exist, using ${INIT_SYSTEM_SYSV} handling as a fallback"
+    fi
+  else
+    readonly INIT_SYSTEM=${INIT_SYSTEM_SYSV}
+    if ! version="$(init --version 2>&1)"; then
+      if ! version="$(chkconfig --version 2>&1)"; then
+        version="$(head -n1 /etc/inittab 2>&1)"
+      fi
+    fi
+  fi
+
+  readonly INIT_SYSTEM_VERSION="$(printf '%s' "${version}" 2>/dev/null | head -n1)"
+}
+
+setLocationOfInitScripts() {
+  toLogInfo "Determining location of init scripts..."
+
+  if [ "${INIT_SYSTEM}" = "${INIT_SYSTEM_SYSTEMD}" ] || [ "${ARCH_ARCH}" = "AIX" ]; then
+    readonly INIT_DIR="${AGENT_SCRIPTS_DIR}"
+  else
+    if [ -d "/etc/init.d" ]; then
+      readonly INIT_DIR="/etc/init.d"
+    elif [ -d "/sbin/init.d" ]; then
+      readonly INIT_DIR="/sbin/init.d"
+    elif [ -d "/etc/rc.d" ]; then
+      readonly INIT_DIR="/etc/rc.d"
+    else
+      return 1
+    fi
+  fi
+
+  toLogInfo "Location of init scripts ${INIT_DIR}"
+  return 0
+}
+
+detectArchitecture() {
+  local detected_arch=
+  if isAvailable arch; then
+    detected_arch="$(arch | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  if [ -z "${detected_arch}" ]; then
+    detected_arch="$(uname -m | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${detected_arch}"
+}
+
+################################################################################
+#	Misc functions
+################################################################################
+
+getAgentCtlBinPath() {
+  printf "%s" "${AGENT_TOOLS_DIR}/lib64/${AGENT_CTL_BIN}"
+}
+
+getOsConfigBinPath() {
+  printf "%s" "${AGENT_INSTALL_DIR}/lib64/${AGENT_OS_CONFIG_BIN}"
+}
+
+commandErrorWrapper() {
+  set +e
+  local command="${*}"
+  local errorFile="/tmp/smartagent_commanderror_$$"
+
+  ${command} 2>"${errorFile}"
+  local returnCode=$?
+  if [ ${returnCode} -ne 0 ]; then
+    toLogWarning "Command '${command}' failed, return code: ${returnCode}, message: $(cat "${errorFile}")"
+  fi
+
+  ${SAFE_RM_EXEC} -f "${errorFile}"
+  set -e
+  return ${returnCode}
+}
+
+checkRootAccess() {
+  toConsoleInfo "Checking root privileges..."
+
+  if [ "$(id -u)" != "0" ]; then
+    toConsoleError "NOT OK"
+    return 1
+  fi
+
+  toConsoleInfo "OK"
+  return 0
+}
+
+removeIfExists() {
+  local pathToRemove="${1}"
+  toLogInfo "${SAFE_RM_EXEC} -rf ${pathToRemove}"
+  if [ ! -e "${pathToRemove}" ]; then
+    toLogInfo "${pathToRemove} does not exist, skipping removal"
+    return
+  fi
+
+  local output
+  if ! output="$(${SAFE_RM_EXEC} -rf "${pathToRemove}" 2>&1)"; then
+    toLogWarning "Failed to remove ${pathToRemove}: ${output}"
+  fi
+}
+
+################################################################################
+#	SELinux related functions
+################################################################################
+
+executeUsingOsConfigBin() {
+  local command="${1}"
+  local unit="${2}"
+  if [ "${unit}" ]; then
+    command="${command}-${unit}"
+    unit=""
+  fi
+
+  local output=
+  output="$("$(getOsConfigBinPath)" "${command}" 2>&1)"
+  local exitCode=$?
+  toLogAdaptive ${exitCode} "Executed $(getOsConfigBinPath) ${command} ${unit}, exitCode = ${exitCode}, output: ${output}"
+  return ${exitCode}
+}
+
+executeSystemctlCommand() {
+  local command="${1}"
+  local unit="${2}"
+
+  if [ "$(id -u)" != 0 ]; then
+    executeUsingOsConfigBin "${command}" "${unit}"
+    return $?
+  fi
+
+  local output=
+  #== shellcheck disable=SC2086
+  output="$(systemctl "${command}" ${unit} 2>&1)"
+  local exitCode=$?
+
+  if [ ${exitCode} -eq 0 ]; then
+    toLogInfo "Successfully executed: systemctl ${command} ${unit}"
+  else
+    toLogError "Failed to execute: systemctl ${command} ${unit}"
+    toLogError "Command output: ${output}"
+    if [ -n "${unit}" ]; then
+      local reachBackNumSeconds=360
+      toLogError "journalctl output: $(journalctl -u "${unit}" --since=-${reachBackNumSeconds} 2>&1)"
+    fi
+  fi
+
+  return ${exitCode}
+} 2>>"${LOG_FILE}"
+
+executeInitScriptCommand() {
+  local command=
+  local parameters="$*"
+  local output=
+  local exitCode=
+
+  if isAvailable service; then
+    command="service"
+    parameters="${SERVICE_SCRIPT_FILE} ${parameters}"
+  else
+    command="${INIT_DIR}/${SERVICE_SCRIPT_FILE}"
+  fi
+  #shellcheck disable=SC2086
+  output="$("${command}" ${parameters} 2>&1)"
+  exitCode=$?
+  toLogAdaptive ${exitCode} "Executed ${command} ${parameters}, exitCode = ${exitCode}, output: ${output}"
+  return ${exitCode}
+}
+
+signalHandler() {
+	end_exit_code=$?
+	if [ ${end_exit_code} -eq ${EXIT_CODE_SIGNAL_RECEIVED} ]; then
+		return
+	fi
+	local signal="${1}"
+	local callback="${2}"
+	if [ ${end_exit_code} -ne 0 ]; then
+		toLogWarning "Process Received [Signal: ${signal}] [Code : ${end_exit_code}]"
+	fi
+	${callback} ${end_exit_code}
+
+	if [ ${end_exit_code} -eq ${EXIT_CODE_OK} ]; then
+		exit ${EXIT_CODE_OK}
+	fi
+
+	exit ${EXIT_CODE_SIGNAL_RECEIVED}
+}
+
+configureSignalHandling() {
+  local callback="${1}"
+  for signal in HUP INT QUIT ABRT ALRM TERM EXIT; do
+    # shellcheck disable=SC2064
+    trap "signalHandler '${signal}' '${callback}'" ${signal}
+  done
+
+  trap "" PIPE
+}
+
+removeSecretsFromString() {
+  printf "%s" "$*" | sed 's#\(Api-Token=\)[[:alnum:]_-]\{21\}#\1***#' |
+    sed 's#\(TENANT_TOKEN=\)[[:alnum:]]\{16\}#\1***#' |
+    sed 's#\(latest/\)[[:alnum:]]\{16\}#\1***#' |
+    sed 's#\(PROXY=\)[^[:space:]]*#\1***#'
+}
+
+#== waitTime must be divisible by 10
+sendSignalToProcessAndWaitForStop() {
+  local pidCheckingFunction="${1}"
+  local signal="${2}"
+  local action="${3}"
+  local waitTime="${4}"
+  local pidToStop="${5}"
+
+  if ! ${pidCheckingFunction} "${pidToStop}"; then
+    toLogInfo "Process with pid ${pidToStop} doesn't exist"
+    return
+  fi
+
+  toConsoleInfo "Waiting ${waitTime} seconds for process with pid ${pidToStop} to ${action}."
+  while [ "${waitTime}" -gt 0 ]; do
+    if [ "$((waitTime % 10))" -eq 0 ]; then
+      toLogInfo "Sending signal: ${signal} to ${pidToStop}"
+      kill -s "${signal}" "${pidToStop}" 2>>"${LOG_FILE}"
+    fi
+
+    if ! ${pidCheckingFunction} "${pidToStop}"; then
+      return 0
+    fi
+    sleep 1
+    waitTime=$((waitTime - 1))
+  done
+  return 1
+}
+
+testWriteAccessToDir() {
+  local errorFile="/tmp/smartagent_commandError_$$"
+  local dir="${1}"
+  local tmpfilename
+  if [ "${ARCH_ARCH}" = "AIX" ]; then
+    tmpfilename="${dir}/.tmp_${BRAND_PRODUCT_NAME_LOWER}.$$${RANDOM}"
+    touch "${tmpfilename}" 2>"${errorFile}"
+  else
+    tmpfilename="$(mktemp -p "${dir}" ".tmp_${BRAND_PRODUCT_NAME_LOWER}.XXXXXXXXXXXXXX" 2>"${errorFile}")"
+  fi
+
+  #shellcheck disable=SC2181
+  if [ $? -ne 0 ]; then
+    toLogInfo "$(cat "${errorFile}")"
+    ${SAFE_RM_EXEC} -f "${errorFile}"
+    return 1
+  fi
+
+  ${SAFE_RM_EXEC} -f "${tmpfilename}" "${errorFile}"
+  return 0
+}
+
+setPATH() {
+  local prependToPATH="/usr/sbin:/usr/bin:/sbin:/bin"
+  if [ "${PATH}" ]; then
+    PATH=${prependToPATH}:${PATH}
+  else
+    PATH=${prependToPATH}
+  fi
+}
+
+systemLibDirSanityCheck() {
+  local dir="${1}"
+  if [ ! -d "${dir}" ]; then
+    toLogWarning "Directory: ${dir} does not exist"
+    printf ""
+    return
+  fi
+
+  if ! testWriteAccessToDir "${dir}"; then
+    toLogWarning "Detected that ${dir} is not writable"
+    dir="${PA_FALLBACK_INSTALLATION_DIR}${dir}"
+    createDirIfNotExistAndSetRights "${dir}" 755
+  fi
+
+  printf "%s" "${dir}"
+}
+
+isNonRootModeEnabled() {
+  printf '%s' "${PARAM_NON_ROOT_MODE}" | grep -qE "(true|no_ambient)"
+}
+
+runAutostartAddingTool() {
+  local prefix="${1}"
+  local file="${2}"
+  local suffix="${3}"
+  local output
+
+  if isNonRootModeEnabled && printf '%s' "${prefix}" | grep -q "chkconfig"; then
+    local action="chkconfig-add"
+    if printf '%s' "${prefix}" | grep -q -- "--del"; then
+      action="chkconfig-del"
+    fi
+    toLogInfo "Using ${AGENT_OS_CONFIG_BIN} ${action}-${file} to modify autostart"
+    output="$("$(getOsConfigBinPath)" "${action}-${file}" 2>&1)"
+  else
+    local command="${prefix}${file} ${suffix}"
+    toLogInfo "Executing ${command}"
+    output="$(${command} 2>&1)"
+  fi
+  local status=$?
+
+  if [ "${output}" ]; then
+    toLogAdaptive ${status} "${output}"
+  fi
+
+  return ${status}
+}
+
+readLinkFromLs() {
+  local path="${1}"
+  local result="${path}"
+  local lsOutput="$(ls -dl "${path}" 2>/dev/null)"
+  if printf '%s' "${lsOutput}" | grep -q " -> "; then
+    local parsedLinkTarget="$(printf '%s' "${lsOutput}" | sed 's|^.* -> ||')"
+    if [ "${parsedLinkTarget}" ]; then
+      result="${parsedLinkTarget}"
+    else
+      toLogWarning "Failed to parse ls output '${lsOutput}'"
+    fi
+  fi
+  printf '%s' "${result}"
+}
+
+readLink() {
+  local args=-e
+  local path="${1}"
+  if [ "${2}" ]; then
+    args="${1}"
+    path="${2}"
+  fi
+
+  (
+    if [ "${ARCH_ARCH}" = "AIX" ]; then
+      PATH="${PATH}:/opt/freeware/bin"
+    fi
+
+    if isAvailable readlink; then
+      #shellcheck disable=SC2086
+      readlink ${args} "${path}"
+    else
+      toLogInfo "readlink command not found, falling back to parsing ls output"
+      readLinkFromLs "${path}"
+    fi
+  )
+}
+
+isAvailable() {
+  command -v "${1}" >/dev/null 2>&1
+}
+
+getValueFromConfigFile() {
+  local key="${1}"
+  local separator="${2}"
+  local configFile="${3}"
+  local defaultValue="${4}"
+
+  local value="$(sed -n "s|${key}${separator}||p" "${configFile}" 2>/dev/null)"
+
+  if [ "${value}" ]; then
+    printf '%s' "${value}"
+  else
+    printf '%s' "${defaultValue}"
+  fi
+}
+
+isAnotherInstallerRunning() {
+  if [ ! -f "${INSTALLER_LOCK_FILE}" ]; then
+    return 1
+  fi
+
+  local pidFromLockFile="$(head -n 1 "${INSTALLER_LOCK_FILE}")"
+  local nameFromLockFile="$(tail -n 1 "${INSTALLER_LOCK_FILE}")"
+  if [ "$(wc -l <"${INSTALLER_LOCK_FILE}")" -ne 2 ] || [ -z "${pidFromLockFile}" ] || [ -z "${nameFromLockFile}" ]; then
+    toConsoleWarning "Installer lock file ${INSTALLER_LOCK_FILE} is damaged, skipping uniqueness check."
+    toConsoleWarning "Lock file contents: '$(cat ${INSTALLER_LOCK_FILE})'"
+    return 1
+  fi
+
+  #shellcheck disable=SC2009
+  local foundProcesses="$(ps -e -o "pid,args" 2>&1 | grep -w "${nameFromLockFile}" | grep -v " grep ")"
+  if printf '%s' "${foundProcesses}" | awk '{ print $1 }' | grep -wq "${pidFromLockFile}"; then
+    local errorMessage="Another ${BRAND_PRODUCT_NAME} installer or uninstaller is already running"
+    if printf '%s' "${foundProcesses}" | grep -q "${DOWNLOADS_DIRECTORY}"; then
+      errorMessage="${errorMessage} (AutoUpdate is in progress)"
+    fi
+
+    toConsoleError "${errorMessage}, PID ${pidFromLockFile}. Exiting."
+    return 0
+  fi
+
+  toConsoleInfo "Lock file exists but corresponding installer process does not run, contents of lock file: ${pidFromLockFile}, ${nameFromLockFile}."
+  return 0
+}
+
+createInstallerLockFile() {
+  printf '%s\n%s\n' "$$" "$0" >"${INSTALLER_LOCK_FILE}" 2>/dev/null
+}
+
+removeInstallerLockFile() {
+  toLogInfo "Removing installer lock file."
+  ${SAFE_RM_EXEC} -f "${INSTALLER_LOCK_FILE}"
+}
+
+logBasicStartupInformation() {
+  toLogInfo "Command line: $(removeSecretsFromString "${@}")"
+  toLogInfo "Shell options: $-"
+  toLogInfo "Working dir: $(pwd)"
+  toLogInfo "PID: $$"
+  toLogInfo "Parent process: $(
+    printf '\n'
+    ps -o user,pid,ppid,comm -p ${PPID} 2>&1
+  )"
+  toLogInfo "User id: $(id -u)"
+}
+
+mapPidsToName() {
+  local pids="${1}"
+  local output
+  for pid in ${pids}; do
+    local name="$(grep 'Name:' "/proc/${pid}/status" 2>/dev/null | awk '{print $2}')"
+    output="${output}, ${pid} (${name})"
+  done
+
+  printf '%s' "${output}" | cut -c 3-
+}
+
+readonly ARCH_ARCH="X86"
+readonly ARCH_VERSIONED_LIB_DIR_PREFIX="linux-x86"
+
+arch_checkArchitectureCompatibility() {
+  local arch="$(detectArchitecture)"
+  if [ "${arch}" = "X86_64" ] || [ "${arch}" = "IA64" ]; then
+    arch="X86_64"
+  else
+    arch="$(uname -m | sed -e 's/i.86/x86/' | sed -e 's/i86pc/x86/' | tr '[:lower:]' '[:upper:]')"
+  fi
+
+  printf '%s' "${arch}"
+  [ "${arch}" = "X86_64" ]
+}
+
+arch_local_getLibraryPathFromLdd() {
+  local binary="${1}"
+  ldd "${binary}" 2>/dev/null | grep libc.so | awk '{ print $3 }'
+}
+
+arch_local_detectProcessAgentInstallerDirectories() {
+  local useLddOutput="false"
+
+  local systemLib32Prefix
+  systemLib32Prefix="$(arch_local_getSystemLibraryPath 32)"
+  local exitCode=$?
+
+  if [ ! "${systemLib32Prefix}" ]; then
+    if [ "${exitCode}" -eq 0 ]; then
+      toLogWarning "This is a 64-bit platform with 32-bit libraries installed, but ${AGENT_INSTALL_ACTION_BIN} failed to determine their location"
+      useLddOutput="true"
+    else
+      toLogInfo "This is a 64-bit platform and 32-bit libraries were not detected"
+    fi
+  else
+    systemLib32Prefix="$(systemLibDirSanityCheck "/${systemLib32Prefix}")"
+    if [ ! "${systemLib32Prefix}" ]; then
+      useLddOutput="true"
+    fi
+  fi
+
+  local systemLib64Prefix="$(arch_local_getSystemLibraryPath 64)"
+  if [ ! "${systemLib64Prefix}" ]; then
+    toLogWarning "This is a 64-bit platform, but ${AGENT_INSTALL_ACTION_BIN} failed to determine location of 64-bit libraries"
+    useLddOutput="true"
+  else
+    systemLib64Prefix="$(systemLibDirSanityCheck "/${systemLib64Prefix}")"
+    if [ ! "${systemLib64Prefix}" ]; then
+      useLddOutput="true"
+    fi
+  fi
+
+  if [ "${useLddOutput}" = "true" ]; then
+    arch_local_detectProcessAgentDirectoriesBasedOnLdd
+  else
+    readonly SYSTEM_LIB32="${systemLib32Prefix}"
+    readonly SYSTEM_LIB64="${systemLib64Prefix}"
+  fi
+} 2>>"${LOG_FILE}"
+
+arch_detectProcessAgentInstallerDirectories() {
+  if [ "${CONTAINER_DEPLOYMENT_STATE_ENTRY}"x = "true"x ]; then
+    readonly SYSTEM_LIB32="${PA_FALLBACK_INSTALLATION_DIR}/lib32"
+    readonly SYSTEM_LIB64="${PA_FALLBACK_INSTALLATION_DIR}/lib64"
+    createDirIfNotExistAndSetRights "${SYSTEM_LIB32}" 755
+    createDirIfNotExistAndSetRights "${SYSTEM_LIB64}" 755
+    return 0
+  fi
+
+  arch_local_detectProcessAgentInstallerDirectories
+}
+
+arch_getLibMacro() {
+  local libMacro=""
+  if [ "${SYSTEM_LIB32}" ]; then
+    #shellcheck disable=SC2016
+    libMacro='/$LIB'
+  fi
+  printf "%s" "${libMacro}"
+}
+
+arch_checkGlibc() {
+  local glibcVersion="$(ldd --version | awk 'NR==1{ print $NF }')"
+
+  toLogInfo "Detected glibc version: ${glibcVersion}"
+
+  if [ "$(format_version "${glibcVersion}")" -gt "$(format_version "${GLIBC_SUPPORTED_VERSION}")" ]; then
+    return
+  elif [ "$(format_version "${glibcVersion}")" -lt "$(format_version "${GLIBC_SUPPORTED_VERSION}")" ]; then
+    toConsoleError "We can't continue setup. The glibc version: ${glibcVersion} detected on your system isn't supported."
+    toConsoleError "To install ${BRAND_AGENT_PRODUCT_NAME} you need at least glibc ${GLIBC_SUPPORTED_VERSION}."
+    toConsoleError "Stopping installer process..."
+    finishInstaller "${EXIT_CODE_GLIBC_VERSION_TOO_LOW}"
+  fi
+
+  if ! isAvailable rpm; then
+    toLogInfo "RPM not detected - skipping glibc patch version check."
+    return
+  fi
+
+  local glibcFullVersion="$(arch_runCommandWithTimeout 60 "rpm" "-q" "glibc" | tail -n 1)"
+  if [ ! "${glibcFullVersion}" ]; then
+    toConsoleError "Could not determine exact glibc version using RPM."
+    finishInstaller "${EXIT_CODE_GLIBC_VERSION_TOO_LOW}"
+  fi
+
+  # fix for RedHat 5.x with unsufficient glibc 2.5 patch version
+  local glibcPatchVersion="$(format_patch "${glibcFullVersion}")"
+  if [ "$(format_version "${glibcVersion}")" -eq "$(format_version "${GLIBC_SUPPORTED_VERSION}")" ] && [ "${glibcPatchVersion}" -lt "${GLIBC_SUPPORTED_VERSION_MINIMAL_PATCH}" ]; then
+    toConsoleError "glibc patch version: ${glibcPatchVersion} detected on your system is not supported, setup won't continue."
+    toConsoleError "To install ${BRAND_AGENT_PRODUCT_NAME} you need at least glibc ${GLIBC_SUPPORTED_VERSION}-${GLIBC_SUPPORTED_VERSION_MINIMAL_PATCH}."
+    finishInstaller "${EXIT_CODE_GLIBC_VERSION_TOO_LOW}"
+  fi
+}
+
+# 'timeout' requires gnu-coreutils8, i.e. it is not available on RHEL5, that's why we need this utility function
+arch_runCommandWithTimeout() {
+  local commandTimeout="${1}"
+  shift
+  local resultFile="/tmp/smartagent_commandResult_$$"
+  local errorFile="/tmp/smartagent_commandError_$$"
+  local loopErrorsFile="/tmp/smartagent_loopErrors_$$"
+
+  toLogInfo "Executing $* with timeout ${commandTimeout} seconds"
+  (
+    "$@" >${resultFile} 2>${errorFile} &
+    child=$!
+    while [ "${commandTimeout}" -gt 0 ]; do
+      toLogInfo "Time left: ${commandTimeout} s,  pid: ${child}"
+      sleep 1
+      kill -0 "${child}" 2>/dev/null || exit 0
+      commandTimeout=$((commandTimeout - 1))
+    done
+
+    # Be nice, post SIGTERM first.
+    # The 'exit 0' below will be executed if any preceeding command fails.
+    toLogInfo "Killing ${child} with SIGTERM"
+    kill -s 15 "${child}" && kill -0 "${child}" || exit 0
+    sleep 1
+    toLogWarning "Killing ${child} with SIGKILL"
+    kill -s 9 "${child}"
+  ) 2>"${loopErrorsFile}"
+
+  local errorOutput="$(cat "${errorFile}")"
+  local commandOutput="$(cat "${resultFile}")"
+  local loopErrors="$(cat "${loopErrorsFile}")"
+  ${SAFE_RM_EXEC} -f "${errorFile}" "${resultFile}" "${loopErrorsFile}"
+
+  if [ -n "${errorOutput}" ]; then
+    toLogInfo "Failed to execute command, error output: ${errorOutput}"
+    if [ "${loopErrors}" ]; then
+      toLogInfo "Loop errors: ${loopErrors}"
+    fi
+    printf ""
+    return 1
+  else
+    toLogInfo "Command executed successfully, output: ${commandOutput}"
+    if [ "${loopErrors}" ]; then
+      toLogInfo "Loop errors: ${loopErrors}"
+    fi
+    printf '%s' "${commandOutput}"
+    return 0
+  fi
+}
+
+arch_getAccessRights() {
+  stat --format='%A' "${1}"
+}
+
+arch_preloadTest() {
+  if [ "${SYSTEM_LIB32}" ]; then
+    runSanityCheckCommand "performLdPreloadSanityCheck" "32"
+    local returnCode=$?
+    if [ ${returnCode} -ne 0 ]; then
+      return ${returnCode}
+    fi
+  fi
+
+  if [ "${SYSTEM_LIB64}" ]; then
+    runSanityCheckCommand "performLdPreloadSanityCheck" "64"
+  fi
+}
+
+arch_checkEnvironmentConfiguration() {
+  if ! stat --format='%t,%T' /dev/null | grep -q "1,3"; then
+    toLogInfo "$(stat /dev/null)"
+    toConsoleError "Installer detected corruption of '/dev/null': Not a character device"
+    return "${EXIT_CODE_MISCONFIGURED_ENVIRONMENT}"
+  fi
+
+  return 0
+}
+
+arch_moveReplaceTarget() {
+  local source="${1}"
+  local target="${2}"
+  mv -fT "${source}" "${target}"
+}
+
+initLog() {
+  if [ "${CONTAINER_DEPLOYMENT_STATE_ENTRY}"x != "true"x ]; then
+    createLogDirsIfMissing
+  fi
+
+  toLogInfo "Uninstaller started."
+  toLogInfo "Distribution: $(detectLinuxDistribution)"
+  logBasicStartupInformation "${@}"
+}
+
+finishUninstaller() {
+  removeInstallerLockFile
+  if [ -z "$(ls -A ${INSTALL_DIR})" ];then
+    ${SAFE_RM_EXEC} -df ${INSTALL_DIR}
+    ${SAFE_RM_EXEC} -f ${CURRENT_VERSION_LINK}
+  fi
+  if [ $1 -eq 0 ]; then
+    toConsoleInfo "Uninstaller finished successfully"
+  else
+    toConsoleInfo "Uninstaller finished failed"
+  fi
+  exit "${EXIT_CODE_OK}"
+}
+
+setLogFilePath() {
+    #== - 反安装日志: uninstallation-{timestamp}.log 如: uninstallation-20220601110912.log
+    readonly LOG_FILE="${INSTALLER_LOG_DIR}/uninstallation-"$(date -u +"%Y%m%d%H%M%S")".log"
+}
+
+removeScriptsFromAutostart() {
+  local prefix="${1}"
+  local suffix="${2}"
+
+  if ! runAutostartAddingTool "${prefix}" "${SERVICE_SCRIPT_FILE}" "${suffix}"; then
+    toConsoleError "Failed to remove ${SERVICE_SCRIPT_FILE} from autostart. For details, see ${LOG_FILE}"
+  fi
+}
+
+removeUnitFromSystemd() {
+  local unit=${1}
+  if [ ! -e "${SYSTEMD_UNIT_FILES_DIR}/${unit}" ]; then
+    toLogInfo "${unit} does not exist and will not be removed from ${INIT_SYSTEM_SYSTEMD} autostart"
+    return
+  fi
+
+  executeSystemctlCommand disable "${unit}"
+  ${SAFE_RM_EXEC} -f "${SYSTEMD_UNIT_FILES_DIR}/${unit}"
+}
+
+removeSystemvAutostart() {
+  toConsoleInfo "Removing ${BRAND_AGENT_PRODUCT_NAME} from autostart"
+
+  #Order of checking is important
+  if [ -x /usr/bin/update-rc.d ]; then #Ubuntu
+    removeScriptsFromAutostart "/usr/bin/update-rc.d -f " "remove"
+  elif [ -x /usr/sbin/update-rc.d ]; then #Ubuntu
+    removeScriptsFromAutostart "/usr/sbin/update-rc.d -f " "remove"
+  elif [ -x /sbin/chkconfig ]; then #RedHat
+    removeScriptsFromAutostart "/sbin/chkconfig --del "
+  elif [ -x /usr/lib/lsb/install_initd ]; then #Suse
+    removeScriptsFromAutostart "/usr/lib/lsb/install_initd ${INIT_DIR}/"
+  elif [ "${ARCH_ARCH}" = "AIX" ]; then
+    #== 不存在
+    arch_removeAutostart
+  else
+    toConsoleError "Couldn't remove ${BRAND_AGENT_PRODUCT_NAME} from autostart."
+  fi
+}
+
+removeSystemdAutostart() {
+  removeUnitFromSystemd "${SYSTEMD_UNIT_FILE_AGENT}"
+  executeSystemctlCommand daemon-reload ""
+}
+
+stopAgentService() {
+  if [ -s "${AGENT_PID_FILE}" ]; then
+    toLogInfo "Stop ${AGENT_PROC}..."
+    commandErrorWrapper ${AGENT_BIN_DIR}/agentctl stop
+  fi
+}
+
+removeSystemvInitScripts() {
+  if [ "${ARCH_ARCH}" = "AIX" ]; then
+    return
+  fi
+
+  toConsoleInfo "Removing ${SERVICE_SCRIPT_FILE} from ${INIT_DIR}"
+  ${SAFE_RM_EXEC} -f "${INIT_DIR}/${SERVICE_SCRIPT_FILE}"
+}
+
+removeAutostartScripts() {
+  if [ "${INIT_SYSTEM}" = "${INIT_SYSTEM_SYSV}" ]; then
+    removeSystemvAutostart
+    removeSystemvInitScripts
+  else
+    removeSystemdAutostart
+  fi
+}
+
+createUninstallInfoFile() {
+  local reason
+  if [ "${IS_UPGRADING}"x = "true"x ]; then
+    reason="upgrade"
+  else
+    reason="uninstall"
+  fi
+
+  toLogInfo "Creating uninstall.info file: ${reason}"
+}
+
+initializeUninstaller() {
+  local installerPid=
+  local skipRootPrivilegesCheck=
+
+  if [ $# -eq 1 ]; then
+    installerPid="${1}"
+  fi
+  if [ $# -eq 2 ]; then
+     installerPid="${1}"
+     skipRootPrivilegesCheck="${2}"
+  fi
+
+  setPATH
+
+  #setLogFilePath "${installerPid}"
+  setLogFilePath
+
+  if [ "${installerPid}" ] && [ "${installerPid}"x != "online"x ]; then
+    IS_UPGRADING="true"
+  else
+    IS_UPGRADING="false"
+  fi
+  toConsoleInfo "IS_UPGRADING :${IS_UPGRADING}"
+
+  if [ "${skipRootPrivilegesCheck}"x = "true"x ] && [ "${IS_UPGRADING}"x = "true"x ]; then
+    toConsoleInfo "Skipping root privileges check"
+  else
+    if ! checkRootAccess; then
+      toConsoleError "You must run uninstaller with root privileges"
+      exit "${EXIT_CODE_GENERIC_ERROR}"
+    fi
+  fi
+
+  initLog "${@}"
+
+  toLogInfo "Launched during upgrade: ${IS_UPGRADING}"
+  createUninstallInfoFile
+
+  configureSignalHandling "finishUninstaller"
+
+  if [ "${IS_UPGRADING}"x = "false"x ] && [ "${CONTAINER_DEPLOYMENT_STATE_ENTRY}"x != "true"x ]; then
+    if isAnotherInstallerRunning; then
+      exit "${EXIT_CODE_ANOTHER_INSTALLER_RUNNING}"
+    fi
+    createInstallerLockFile
+  fi
+
+  if [ ! -f ${PERL_FILE} ]; then
+    toConsoleInfo "/usr/bin/perl file not found so ${SAFE_RM_EXEC} is used."
+  fi
+
+  toConsoleInfo "Using ${SAFE_RM_EXEC} to uninstall"
+}
+
+getVolumeStorageAgentInstallDir() {
+  readLink "$(dirname "${0}")"
+}
+
+cleanUpSELinux() {
+  if [ ! "${ARCH_HAS_SELINUX}" ] || ! isAvailable semodule; then
+    return
+  fi
+
+  toConsoleInfo "Removing ${BRAND_FORMAL_NAME} SELinux policies, this may take a while..."
+}
+
+cleanUpFilesDuringUninstall() {
+	toLogInfo "This is a full uninstaller, cleaning up rest of the files"
+
+  local fileNames="${AGENT_CONF_DIR} ${AGENT_BIN_DIR} ${LOG_DIR} ${AGENT_SCRIPTS_DIR} ${AGENT_LIBS_DIR}"
+  for fileName in ${fileNames}; do
+	  removeIfExists "${fileName}"
+  done
+
+  fileNames="uninstall.sh installer.version"
+  for fileName in ${fileNames}; do
+	  removeIfExists "${INSTALL_DIR}/${fileName}"
+  done
+
+}
+
+handleRegularUninstaller() {
+
+  checkInitSystem
+  toLogInfo "Detected init system: ${INIT_SYSTEM}, version: ${INIT_SYSTEM_VERSION}"
+  if ! setLocationOfInitScripts; then
+    toLogWarning "Cannot determine location of init scripts."
+  fi
+
+  stopAgentService
+
+  cleanUpFilesDuringUninstall
+
+}
+
+main() {
+  initializeUninstaller "$@"
+  handleRegularUninstaller "$@"
+#  finishUninstaller
+}
+
+################################################################################
+#
+# Script start
+#
+################################################################################
+main "$@"

BIN
dist/scripts/xzdec


+ 1 - 0
ebpftracer/ebpf/include/socket_trace_common.h

@@ -191,6 +191,7 @@ struct ebpf_proc_info {
 	__u64 start_addr;
 	__u64 end_addr;
 	unsigned char instance_id[APM_INSTANCE_ID_SIZE];
+	unsigned char app_id[APM_APP_ID_SIZE];
 	__u16 code_type;
 
 	__u64 method_ptr_pos;

+ 2 - 1
ebpftracer/ebpf/utrace/go/net/client.probe.bpf.c

@@ -647,7 +647,7 @@ int uprobe_Transport_roundTrip(struct pt_regs *ctx) {
 ////			cw_bpf_debug("[Client] host_id:%02x", trace_conf->host_id[i]);
 //		}
 		copy_byte_arrays(trace_conf->host_id, httpReq->apm_sc.host_id, APM_HOST_ID_SIZE);
-		copy_byte_arrays(trace_conf->app_id, httpReq->apm_sc.app_id, APM_APP_ID_SIZE);
+//		copy_byte_arrays(trace_conf->app_id, httpReq->apm_sc.app_id, APM_APP_ID_SIZE);
 	}
 
 	// __u64 pid_tgid = bpf_get_current_pid_tgid();
@@ -660,6 +660,7 @@ int uprobe_Transport_roundTrip(struct pt_regs *ctx) {
 //		}
 //	}
 	copy_byte_arrays(proc_info->instance_id, httpReq->apm_sc.instance_id, APM_APP_ID_SIZE);
+	copy_byte_arrays(proc_info->app_id, httpReq->apm_sc.app_id, APM_APP_ID_SIZE);
 
 	// set assumed_app_id
 	set_assumed_app_id_arrays(httpReq->host, httpReq->apm_sc.assumed_app_id, APM_ASSUMED_APP_ID_STRING_SIZE);

+ 2 - 1
ebpftracer/ebpf/utrace/java/net/client.probe.bpf.c

@@ -89,7 +89,7 @@ static __inline struct apm_span_context *build_sc(struct ebpf_proc_info *proc_in
 //			cw_bpf_debug("[Client] host_id:%02x", trace_conf->host_id[i]);
 //		}
 		copy_byte_arrays(trace_conf->host_id, cw_sc->host_id, APM_HOST_ID_SIZE);
-		copy_byte_arrays(trace_conf->app_id, cw_sc->app_id, APM_APP_ID_SIZE);
+//		copy_byte_arrays(trace_conf->app_id, cw_sc->app_id, APM_APP_ID_SIZE);
 	}
 
 //	__u64 pid_tgid = bpf_get_current_pid_tgid();
@@ -98,6 +98,7 @@ static __inline struct apm_span_context *build_sc(struct ebpf_proc_info *proc_in
 //			bpf_map_lookup_elem(&proc_info_map, &tgid);
 	if (proc_info) {
 		copy_byte_arrays(proc_info->instance_id, cw_sc->instance_id, APM_APP_ID_SIZE);
+		copy_byte_arrays(proc_info->app_id, cw_sc->app_id, APM_APP_ID_SIZE);
 	}
 
 	// set assumed_app_id

+ 1 - 1
ebpftracer/init.go

@@ -4,7 +4,7 @@ import (
 	"strings"
 
 	"github.com/coroot/coroot-node-agent/proc"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 type file struct {

+ 9 - 12
ebpftracer/jvm.go

@@ -2,13 +2,13 @@ package ebpftracer
 
 import (
 	"errors"
+	"github.com/coroot/coroot-node-agent/ebpftracer/tracer/inject"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
+	klog "github.com/sirupsen/logrus"
 	"io/ioutil"
 	"runtime"
 	"strings"
 
-	"github.com/coroot/coroot-node-agent/common"
-	"github.com/coroot/coroot-node-agent/ebpftracer/tracer/inject"
-
 	"debug/elf"
 	"fmt"
 	"path/filepath"
@@ -25,7 +25,7 @@ const (
 	symbolsocketRead0 = "Java_sun_nio_ch_FileDispatcherImpl_read0"
 )
 
-func (t *Tracer) AttachJavaNioReadUprobes(pid uint32, codeType common.CodeType) ([]link.Link, error) {
+func (t *Tracer) AttachJavaNioReadUprobes(pid uint32, codeType CodeType) ([]link.Link, error) {
 	if t.DisableL7Tracing() {
 		return nil, nil
 	}
@@ -37,15 +37,13 @@ func (t *Tracer) AttachJavaNioReadUprobes(pid uint32, codeType common.CodeType)
 		bpath = proc.Path(uint32(pid), "exe")
 	} else {
 		version := UsePIDToGetJDKVersion(pid)
-		fmt.Println("java version is ", version)
-
+		klog.Infof("[attach] java version is %s", version)
 		bpath = getSoPath(pid, "libnio.so")
 		if bpath == "" {
 			return nil, errors.New("can not find nio.so")
 		}
 	}
-
-	fmt.Println("find the nio.so path is ", bpath)
+	klog.Infof("[attach] find the nio.so path is  %s", bpath)
 	ex, err := link.OpenExecutable(bpath)
 	if err != nil {
 		return nil, err
@@ -103,7 +101,7 @@ func (t *Tracer) AttachJavaNioReadUprobes(pid uint32, codeType common.CodeType)
 				break
 			}
 		}
-		fmt.Println("s.Name-----:", s.Name)
+		klog.Infof("[attach] java symbol name is  %s", s.Name)
 		switch s.Name {
 		case symbolsocketRead0:
 			sStart := s.Value - textSection.Addr
@@ -129,7 +127,7 @@ func (t *Tracer) AttachJavaNioReadUprobes(pid uint32, codeType common.CodeType)
 	if len(links) == 0 {
 		return nil, fmt.Errorf("no links found for %s", bpath)
 	}
-	fmt.Println("[jvm] libnio attached, pid is ", pid)
+	klog.WithField("pid", pid).Infof("[attach] libnio attached!")
 	return links, nil
 }
 
@@ -180,7 +178,6 @@ func (t *Tracer) AttachJavaNetWriteUprobes(pid uint32) ([]link.Link, error) {
 	}
 
 	err := inject.JvmInject(jvmInjector)
-	fmt.Println("AttachJavaNetWriteUprobes:", err)
 	if err != nil {
 		return nil, err
 	}
@@ -204,7 +201,7 @@ func (t *Tracer) AttachJavaNetWriteUprobes(pid uint32) ([]link.Link, error) {
 	if len(links) == 0 {
 		return nil, errors.New("can not find uprobe_Java_net_SocketOutputStream_socketWrite0")
 	}
-	fmt.Println("[jvm] libnet attached", pid)
+	klog.WithField("pid", pid).Infoln("[jvm] libnet attached")
 	return links, nil
 }
 

+ 12 - 14
ebpftracer/netcore.go

@@ -15,8 +15,6 @@ import (
 	debugelf "debug/elf"
 	"fmt"
 
-	"github.com/coroot/coroot-node-agent/utils"
-
 	"github.com/cilium/ebpf/link"
 )
 
@@ -166,9 +164,9 @@ func (t *Tracer) getFunctionOffsetDBG(libPath, functionName string) (elf.Symbol,
 	return elf.Symbol{}, fmt.Errorf("function %s not found", functionName)
 }
 
-func (t *Tracer) AttachNetCoreNetReadUprobes(pid uint32, insID utils.ID) []link.Link {
+func (t *Tracer) AttachNetCoreNetReadUprobes(pid uint32) ([]link.Link, error) {
 	if t.DisableL7Tracing() {
-		return nil
+		return nil, nil
 	}
 
 	var links []link.Link
@@ -180,21 +178,21 @@ func (t *Tracer) AttachNetCoreNetReadUprobes(pid uint32, insID utils.ID) []link.
 	l, err := ex.Uprobe(functionSym.Name, t.uprobes["SystemNative_Receive"], &link.UprobeOptions{Address: functionSym.Value, Offset: 113})
 	if err != nil {
 		fmt.Println("failed to attach SystemNative_Receive uprobe", err, functionSym)
-		return nil
+		return nil, err
 	}
 
 	links = append(links, l)
 
 	if len(links) == 0 {
-		return nil
+		return nil, nil
 	}
 	fmt.Println("netcore uprobes attached, pid is ", pid)
-	return links
+	return links, nil
 }
 
-func (t *Tracer) AttachNetCoreNetWriteUprobes(pid uint32, insID utils.ID) []link.Link {
+func (t *Tracer) AttachNetCoreNetWriteUprobes(pid uint32) ([]link.Link, error) {
 	if t.DisableL7Tracing() {
-		return nil
+		return nil, nil
 	}
 	//
 	//if pid != 251719 {
@@ -206,7 +204,7 @@ func (t *Tracer) AttachNetCoreNetWriteUprobes(pid uint32, insID utils.ID) []link
 	var links []link.Link
 	ex, err := link.OpenExecutable(libnetSo)
 	if err != nil {
-		return nil
+		return nil, err
 	}
 	opt := link.UprobeOptions{
 		Offset: 16,
@@ -214,16 +212,16 @@ func (t *Tracer) AttachNetCoreNetWriteUprobes(pid uint32, insID utils.ID) []link
 	}
 	upread02, err := ex.Uprobe(sys, t.uprobes["netcore_asmnop"], &opt)
 	if err != nil {
-		return nil
+		return nil, err
 	}
 	links = append(links, upread02)
 
 	if len(links) == 0 {
-		return nil
+		return nil, nil
 	}
 	fmt.Println("netcore client uprobes attached", pid)
 
-	return links
+	return links, nil
 }
 
 func contains(array []string, str string) bool {
@@ -264,7 +262,7 @@ func SplitByteByDelimiter(data []byte) []byte {
 	return append(pre, data[:startIndex]...)
 }
 
-func (t *Tracer) AttachNetCoreNetThreadUprobes(pid uint32, insID utils.ID) []link.Link {
+func (t *Tracer) AttachNetCoreNetThreadUprobes(pid uint32) []link.Link {
 	// uprobes := []tracer.Uprobe{}
 
 	binFile, err := os.Open(libPath + ".dbg")

+ 9 - 10
ebpftracer/stack.go

@@ -1,11 +1,10 @@
 package ebpftracer
 
 import (
-	"fmt"
-
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/link"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer"
+	klog "github.com/sirupsen/logrus"
 )
 
 func (t *Tracer) AttachStackUprobes(path string, uprobes []tracer.Uprobe) []link.Link {
@@ -15,10 +14,10 @@ func (t *Tracer) AttachStackUprobes(path string, uprobes []tracer.Uprobe) []link
 	if err != nil {
 		return nil
 	}
-	fmt.Println("Attach Start", path)
+	klog.Infoln("[stack] Attach Start", path)
 
 	for i, up := range uprobes {
-		fmt.Printf("attaching %d -> %d -> %s -> 0x%x -> 0x%x -> 0x%x\n", i, len(uprobes), up.Funcname, up.AbsOffset, up.Address, up.AbsOffset+up.Address)
+		klog.Debugf("[stack] attaching %d -> %d -> %s -> 0x%x -> 0x%x -> 0x%x", i, len(uprobes), up.Funcname, up.AbsOffset, up.Address, up.AbsOffset+up.Address)
 		var prog *ebpf.Program
 		switch up.Location {
 		case tracer.AtEntry:
@@ -30,7 +29,7 @@ func (t *Tracer) AttachStackUprobes(path string, uprobes []tracer.Uprobe) []link
 		}
 		uplink, err := ex.Uprobe(up.Funcname, prog, &link.UprobeOptions{Address: up.Address, Offset: up.AbsOffset})
 		if err != nil {
-			fmt.Printf("attaching ERROR:%v, %v, %v\n", err, up, uplink)
+			klog.Errorf("[stack] attachingERROR:%v, %v, %v", err, up, uplink)
 			// return nil
 		} else {
 			links = append(links, uplink)
@@ -45,7 +44,7 @@ func (t *Tracer) AttachJVMStackUprobes(path string, uprobes []tracer.Uprobe) []l
 	setNodeEnter := "Java_com_cloudwise_agent_common_natives_TraceNative_setNodeEnter"
 	setNodeReturn := "Java_com_cloudwise_agent_common_natives_TraceNative_setNodeReturn"
 
-	fmt.Println("Attach Start AttachJVMStackUprobes", path)
+	klog.Infoln("Attach Start AttachJVMStackUprobes", path)
 
 	var links []link.Link
 	ex, err := link.OpenExecutable(path)
@@ -53,18 +52,18 @@ func (t *Tracer) AttachJVMStackUprobes(path string, uprobes []tracer.Uprobe) []l
 		return nil
 	}
 
-	fmt.Println("Attach Start " + setNodeEnter)
+	klog.Infoln("Attach Start " + setNodeEnter)
 	uplink, err := ex.Uprobe(setNodeEnter, t.uprobes["setNodeEnter"], &link.UprobeOptions{Offset: 0x0})
 	if err != nil {
-		fmt.Printf("attaching ERROR: %v, %v, %v\n", err, setNodeEnter, uplink)
+		klog.Errorf("attaching ERROR: %v, %v, %v\n", err, setNodeEnter, uplink)
 	} else {
 		links = append(links, uplink)
 	}
 
-	fmt.Println("Attach Start " + setNodeReturn)
+	klog.Infoln("Attach Start " + setNodeReturn)
 	uplink, err = ex.Uprobe(setNodeReturn, t.uprobes["setNodeReturn"], &link.UprobeOptions{Offset: 0x0})
 	if err != nil {
-		fmt.Printf("attaching ERROR: %v, %v, %v\n", err, setNodeReturn, uplink)
+		klog.Errorf("attaching ERROR: %v, %v, %v\n", err, setNodeReturn, uplink)
 	} else {
 		links = append(links, uplink)
 	}

+ 71 - 66
ebpftracer/tls.go

@@ -7,19 +7,18 @@ import (
 	"debug/elf"
 	"errors"
 	"fmt"
-	"github.com/coroot/coroot-node-agent/ebpftracer/tracer"
-	"github.com/coroot/coroot-node-agent/utils"
-	"os"
-	"regexp"
-	"strconv"
-	"strings"
-
 	"github.com/cilium/ebpf/link"
+	"github.com/coroot/coroot-node-agent/ebpftracer/tracer"
 	"github.com/coroot/coroot-node-agent/proc"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
+	klog "github.com/sirupsen/logrus"
 	"golang.org/x/arch/arm64/arm64asm"
 	"golang.org/x/arch/x86/x86asm"
 	"golang.org/x/mod/semver"
-	"k8s.io/klog/v2"
+	"os"
+	"regexp"
+	"strconv"
+	"strings"
 )
 
 const (
@@ -36,13 +35,13 @@ var (
 	opensslVersionRe = regexp.MustCompile(`OpenSSL\s(\d\.\d+\.\d+)`)
 )
 
-func (t *Tracer) AttachOpenSslUprobes(pid uint32) []link.Link {
+func (t *Tracer) AttachOpenSslUprobes(pid uint32) ([]link.Link, error) {
 	if t.DisableL7Tracing() {
-		return nil
+		return nil, nil
 	}
 	libPath, version := getSslLibPathAndVersion(pid)
 	if libPath == "" || version == "" {
-		return nil
+		return nil, nil
 	}
 
 	log := func(msg string, err error) {
@@ -52,16 +51,16 @@ func (t *Tracer) AttachOpenSslUprobes(pid uint32) []link.Link {
 					return
 				}
 			}
-			klog.ErrorfDepth(1, "pid=%d libssl_version=%s: %s: %s", pid, version, msg, err)
+			klog.Errorf("pid=%d libssl_version=%s: %s: %s", pid, version, msg, err)
 			return
 		}
-		klog.InfofDepth(1, "pid=%d libssl_version=%s: %s", pid, version, msg)
+		klog.Infof("pid=%d libssl_version=%s: %s", pid, version, msg)
 	}
 
 	exe, err := link.OpenExecutable(libPath)
 	if err != nil {
 		log("failed to open executable", err)
-		return nil
+		return nil, err
 	}
 	var links []link.Link
 	writeEnter := "openssl_SSL_write_enter"
@@ -97,7 +96,7 @@ func (t *Tracer) AttachOpenSslUprobes(pid uint32) []link.Link {
 			l, err := exe.Uprobe(p.symbol, t.uprobes[p.uprobe], nil)
 			if err != nil {
 				//log("failed to attach uprobe", err)
-				return nil
+				return nil, err
 			}
 			links = append(links, l)
 		}
@@ -105,23 +104,24 @@ func (t *Tracer) AttachOpenSslUprobes(pid uint32) []link.Link {
 			l, err := exe.Uretprobe(p.symbol, t.uprobes[p.uretprobe], nil)
 			if err != nil {
 				//log("failed to attach uretprobe", err)
-				return nil
+				return nil, err
 			}
 			links = append(links, l)
 		}
 	}
 
 	//log("libssl uprobes attached", nil)
-	return links
+	return links, nil
 }
 
-func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16) []link.Link {
+func (t *Tracer) AttachGoTlsUprobes(pid uint32, appInfo *AppInfo, codeType uint16) ([]link.Link, error) {
 	if t.DisableL7Tracing() {
-		return nil
+		return nil, nil
 	}
 
 	path := proc.Path(pid, "exe")
-
+	instanceID := appInfo.InstanceIdHash.HashtVal
+	appID := appInfo.AppIdHash.HashtVal
 	var err error
 	var name, version string
 	log := func(msg string, err error) {
@@ -131,33 +131,33 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 					return
 				}
 			}
-			klog.ErrorfDepth(1, "pid=%d golang_app=%s golang_version=%s: %s: %s", pid, name, version, msg, err)
+			klog.Errorf("pid=%d golang_app=%s golang_version=%s: %s: %s", pid, name, version, msg, err)
 			return
 		}
-		klog.InfofDepth(1, "pid=%d golang_app=%s golang_version=%s: %s", pid, name, version, msg)
+		klog.Infof("pid=%d golang_app=%s golang_version=%s: %s", pid, name, version, msg)
 	}
 
 	bi, err := buildinfo.ReadFile(path)
 	if err != nil {
 		log("failed to read build info", err)
-		return nil
+		return nil, err
 	}
 
 	name, err = os.Readlink(path)
 	if err != nil {
 		log("failed to read name", err)
-		return nil
+		return nil, err
 	}
 	version = strings.Replace(bi.GoVersion, "go", "v", 1)
 	if semver.Compare(version, minSupportedGoVersion) < 0 {
 		log(fmt.Sprintf("go_versions below %s are not supported", minSupportedGoVersion), nil)
-		return nil
+		return nil, err
 	}
 
 	ef, err := elf.Open(path)
 	if err != nil {
 		log("failed to open as elf binary", err)
-		return nil
+		return nil, err
 	}
 	defer ef.Close()
 
@@ -165,28 +165,28 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 	if err != nil {
 		if errors.Is(err, elf.ErrNoSymbols) {
 			log("no symbol section", nil)
-			return nil
+			return nil, err
 		}
 		log("failed to read symbols", err)
-		return nil
+		return nil, err
 	}
 
 	textSection := ef.Section(".text")
 	if textSection == nil {
 		log("no text section", nil)
-		return nil
+		return nil, err
 	}
 	textSectionData, err := textSection.Data()
 	if err != nil {
 		log("failed to read text section", err)
-		return nil
+		return nil, err
 	}
 	textSectionLen := uint64(len(textSectionData) - 1)
 
 	exe, err := link.OpenExecutable(path)
 	if err != nil {
 		log("failed to open executable", err)
-		return nil
+		return nil, err
 	}
 
 	offset, ok := tracer.GetOffset(tracer.NewID("std", "runtime", "g", "goid"), path)
@@ -199,24 +199,28 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 			major, err = strconv.Atoi(parts[0])
 			if err != nil {
 				log("Error converting major version:", err)
+				return nil, err
 			}
 			minor, err = strconv.Atoi(parts[1])
 			if err != nil {
 				log("Error converting minor version:", err)
+				return nil, err
 			}
 			revision, err = strconv.Atoi(parts[2])
 			if err != nil {
 				log("Error converting revision version:", err)
+				return nil, err
 			}
 			goVersion := ((major & 0xFF) << 16) + ((minor & 0xFF) << 8) + min(revision, 255)
 
-			info := tracer.EbpfProcInfo{}
+			info := EbpfProcInfo{}
 			info.Version = uint32(goVersion)
-			info.Offsets[tracer.OFFSET_IDX_GOID_RUNTIME_G] = uint16(offset)
+			info.Offsets[OFFSET_IDX_GOID_RUNTIME_G] = uint16(offset)
 			info.NetTCPConnItab = uint64(0)
 			info.CryptoTLSConnItab = uint64(0)
 			info.CredentialsSyscallConnItab = uint64(0)
-			info.InstanceId = insID.HashtVal
+			info.InstanceId = instanceID
+			info.AppId = appID
 			// go
 			info.MethodPtrPos = uint64(0)
 			info.UrlPtrPos = uint64(16)
@@ -243,7 +247,9 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 			_, err = tracer.UpdateProcInfoToMap(t.collection, pid, info)
 			if err != nil {
 				klog.Error("failed to update program info", err)
+				return nil, err
 			}
+			appInfo.EBPFProcInfo = &info
 		}
 	}
 
@@ -277,7 +283,7 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 			if err != nil {
 				log("failed to attach write_enter uprobe", err)
 				klog.Infoln("runtime.execute no")
-				return nil
+				return nil, err
 			} else {
 				klog.Infoln("runtime.execute ok")
 			}
@@ -287,7 +293,7 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 			l, err := exe.Uprobe(s.Name, t.uprobes["enter_runtime_newproc1"], &link.UprobeOptions{Address: address})
 			if err != nil {
 				log("failed to attach newproc1 uprobe", err)
-				return nil
+				return nil, err
 			}
 			links = append(links, l)
 			sStart := s.Value - textSection.Addr
@@ -299,26 +305,23 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
 			if len(returnOffsets) == 0 {
 				log("failed to attach enter_runtime_newproc1 uprobe", fmt.Errorf("no return offsets found"))
-				return nil
+				return nil, err
 			}
 			for _, offset := range returnOffsets {
 				l, err := exe.Uprobe(s.Name, t.uprobes["exit_runtime_newproc1"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
 				if err != nil {
 					log("failed to attach exit_runtime_newproc1 uprobe", err)
-					return nil
+					return nil, err
 				}
 				links = append(links, l)
 			}
 		case goServeHTTP:
 			l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_HandlerFunc_ServeHTTP"], &link.UprobeOptions{Address: address})
 			if err != nil {
-				log("failed to attach write_enter uprobe", err)
-				fmt.Println("net/http.serverHandler.ServeHTTP no")
-				fmt.Println(err)
+				klog.WithError(err).Errorln("failed to attach uprobe_HandlerFunc_ServeHTTP uprobe")
 				continue
-			} else {
-				fmt.Println("net/http.serverHandler.ServeHTTP ok")
 			}
+			klog.Infoln("net/http.serverHandler.ServeHTTP ok")
 			links = append(links, l)
 			sStart := s.Value - textSection.Addr
 			sEnd := sStart + s.Size
@@ -328,14 +331,15 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 			sBytes := textSectionData[sStart:sEnd]
 			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
 			if len(returnOffsets) == 0 {
-				log("failed to attach uprobe_HandlerFunc_ServeHTTP uprobe", fmt.Errorf("no return offsets found"))
-				return nil
+				err = fmt.Errorf("failed to attach uprobe_HandlerFunc_ServeHTTP no return offsets found")
+				klog.Errorln(err)
+				return nil, err
 			}
 			for _, offset := range returnOffsets {
 				l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_HandlerFunc_ServeHTTP_Returns"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
 				if err != nil {
-					log("failed to attach exit_runtime_newproc1 uprobe", err)
-					return nil
+					klog.WithError(err).Errorln(fmt.Errorf("failed to attach exit_runtime_newproc1 uprobe"))
+					return nil, err
 				}
 				links = append(links, l)
 			}
@@ -346,13 +350,12 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 			}
 			l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_Transport_roundTrip"], &link.UprobeOptions{Address: address})
 			if err != nil {
-				log("failed to attach write_enter uprobe", err)
-				fmt.Println("net/http.uprobe_Transport_roundTrip no")
-				fmt.Println(err)
+				klog.WithError(err).Errorln(fmt.Errorf("failed to attach write_enter uprobe"))
 				continue
 			} else {
-				fmt.Println("net/http.uprobe_Transport_roundTrip ok")
 			}
+			klog.Infoln("net/http.uprobe_Transport_roundTrip ok")
+
 			links = append(links, l)
 			sStart := s.Value - textSection.Addr
 			sEnd := sStart + s.Size
@@ -362,14 +365,15 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 			sBytes := textSectionData[sStart:sEnd]
 			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
 			if len(returnOffsets) == 0 {
-				log("failed to attach uprobe_Transport_roundTrip uprobe", fmt.Errorf("no return offsets found"))
-				return nil
+				err = fmt.Errorf("failed to attach uprobe_Transport_roundTrip uprobe no return offsets found")
+				klog.Errorln(err)
+				return nil, err
 			}
 			for _, offset := range returnOffsets {
 				l, err := exe.Uprobe(s.Name, t.uprobes["uprobe_Transport_roundTrip_Returns"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
 				if err != nil {
-					log("failed to attach exit_runtime_newproc1 uprobe", err)
-					return nil
+					klog.WithError(err).Errorln("failed to attach exit_runtime_newproc1 uprobe")
+					return nil, err
 				}
 				links = append(links, l)
 			}
@@ -377,15 +381,15 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 		case goTlsWriteSymbol:
 			l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_write_enter"], &link.UprobeOptions{Address: address})
 			if err != nil {
-				log("failed to attach write_enter uprobe", err)
-				return nil
+				klog.WithError(err).Errorln("failed to attach write_enter uprobe")
+				return nil, err
 			}
 			links = append(links, l)
 		case goTlsReadSymbol:
 			l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_read_enter"], &link.UprobeOptions{Address: address})
 			if err != nil {
-				log("failed to attach read_enter uprobe", err)
-				return nil
+				klog.WithError(err).Errorln("failed to attach read_enter uprobe")
+				return nil, err
 			}
 			links = append(links, l)
 			sStart := s.Value - textSection.Addr
@@ -396,24 +400,25 @@ func (t *Tracer) AttachGoTlsUprobes(pid uint32, insID utils.ID, codeType uint16)
 			sBytes := textSectionData[sStart:sEnd]
 			returnOffsets := getReturnOffsets(ef.Machine, sBytes)
 			if len(returnOffsets) == 0 {
-				log("failed to attach read_exit uprobe", fmt.Errorf("no return offsets found"))
-				return nil
+				err = fmt.Errorf("failed to attach read_exit uprobe no return offsets found")
+				klog.Errorln(err)
+				return nil, err
 			}
 			for _, offset := range returnOffsets {
 				l, err := exe.Uprobe(s.Name, t.uprobes["go_crypto_tls_read_exit"], &link.UprobeOptions{Address: address, Offset: uint64(offset)})
 				if err != nil {
-					log("failed to attach read_exit uprobe", err)
-					return nil
+					klog.WithError(err).Errorln("failed to attach read_exit uprobe")
+					return nil, err
 				}
 				links = append(links, l)
 			}
 		}
 	}
 	if len(links) == 0 {
-		return nil
+		return nil, err
 	}
-	log("crypto/tls uprobes attached", nil)
-	return links
+	klog.Infoln("crypto/tls uprobes attached")
+	return links, nil
 }
 
 func getSslLibPathAndVersion(pid uint32) (string, string) {

+ 34 - 40
ebpftracer/tracer.go

@@ -19,10 +19,10 @@ import (
 	"github.com/coroot/coroot-node-agent/ebpftracer/l7"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer"
 	"github.com/coroot/coroot-node-agent/proc"
-	"github.com/coroot/coroot-node-agent/utils"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
+	klog "github.com/sirupsen/logrus"
 	"golang.org/x/sys/unix"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 
 /*
@@ -230,7 +230,7 @@ type perfMap struct {
 func (t *Tracer) ebpf(ch chan<- Event) error {
 	kv := "v" + common.KernelMajorMinor(t.kernelVersion)
 	path, prg, err := EbpfCode(kv)
-	klog.Infof("kv is %s, kernel version: %s path: %s\n", kv, t.kernelVersion, path)
+	klog.Infof("kv is %s, kernel version: %s path: %s", kv, t.kernelVersion, path)
 	if len(prg) == 0 || err != nil {
 		return fmt.Errorf("kv is %s, unsupported kernel version: %s path: %s err:<%v>", kv, t.kernelVersion, path, err)
 	}
@@ -248,9 +248,11 @@ func (t *Tracer) ebpf(ch chan<- Event) error {
 	_ = unix.Setrlimit(unix.RLIMIT_MEMLOCK, &unix.Rlimit{Cur: unix.RLIM_INFINITY, Max: unix.RLIM_INFINITY})
 	tracer.PidFilter(collectionSpec)
 	opts := &ebpf.CollectionOptions{MapReplacements: make(map[string]*ebpf.Map)}
+	klog.Infof("[start] Look eBPF .maps")
 	for _, spec := range collectionSpec.Maps {
-		fmt.Println("maps:", spec.Name)
+		klog.Infoln(spec.Name)
 	}
+	klog.Infof("[end] Look eBPF .maps")
 	tracer.MapInit(collectionSpec, opts)
 	// TODO 多进程
 	// tracer.SetConstants(collectionSpec)
@@ -274,16 +276,14 @@ func (t *Tracer) ebpf(ch chan<- Event) error {
 		{name: "file_events", typ: perfMapTypeFileEvents, perCPUBufferSizePages: 4},
 		{name: "event_queue", typ: perfMapTypeEventQueue, perCPUBufferSizePages: 32},
 	}
-	fmt.Println(len(collectionSpec.Programs))
-	fmt.Println(len(c.Programs))
 	tracer.MapInsert(c)
 	if !t.DisableL7Tracing() {
 		perfMaps = append(perfMaps, perfMap{name: "l7_events", typ: perfMapTypeL7Events, perCPUBufferSizePages: 32})
 	}
-	perfMaps = append(perfMaps, perfMap{name: tracer.MAP_PERF_SOCKET_DATA_NAME, typ: perfMapTypeSocketEvents, perCPUBufferSizePages: 64})
-	fmt.Println("perfMaps start --")
+	perfMaps = append(perfMaps, perfMap{name: MAP_PERF_SOCKET_DATA_NAME, typ: perfMapTypeSocketEvents, perCPUBufferSizePages: 64})
+	klog.Infof("[start] Look eBPF perf_maps")
 	for _, pm := range perfMaps {
-		fmt.Println("pm.namepm.name: ", pm.name)
+		klog.Infoln(pm.name)
 		m, ok := t.collection.Maps[pm.name]
 		if ok {
 			r, err := perf.NewReader(m, pm.perCPUBufferSizePages*os.Getpagesize())
@@ -296,11 +296,12 @@ func (t *Tracer) ebpf(ch chan<- Event) error {
 			go runEventsReader(pm.name, r, ch, pm.typ)
 		}
 	}
-	fmt.Println("perfMaps end --")
+	klog.Infof("[end] Look eBPF perf_maps")
 
+	klog.Infof("[start] Look eBPF specPrograms")
 	for _, programSpec := range collectionSpec.Programs {
 		program := t.collection.Programs[programSpec.Name]
-		fmt.Println("programSpecprogramSpec:--:", programSpec.Name, programSpec.SectionName, programSpec.Type)
+		klog.Infof("%s:[%s]", programSpec.SectionName, programSpec.Name)
 		if t.DisableL7Tracing() {
 			switch programSpec.Name {
 			case "sys_enter_writev", "sys_enter_write", "sys_enter_sendto", "sys_enter_sendmsg", "sys_enter_sendmmsg":
@@ -321,11 +322,6 @@ func (t *Tracer) ebpf(ch chan<- Event) error {
 			l, err = link.Tracepoint(parts[0], parts[1], program, nil)
 		case ebpf.Kprobe:
 			if strings.HasPrefix(programSpec.SectionName, "uprobe/") {
-
-				fmt.Println("==============uprobe s")
-				fmt.Println(programSpec.Name, programSpec.SectionName, programSpec.Type)
-				fmt.Println("==============uprobe e")
-
 				t.uprobes[programSpec.Name] = program
 				continue
 			}
@@ -337,6 +333,7 @@ func (t *Tracer) ebpf(ch chan<- Event) error {
 		}
 		t.links = append(t.links, l)
 	}
+	klog.Infof("[end] Look eBPF specPrograms")
 
 	return nil
 }
@@ -418,13 +415,13 @@ type l7Event struct {
 	TraceStart          uint32
 	TraceEnd            uint32
 	EventCount          uint32
-	AssumedAppId        utils.HashByte
-	SpanId              utils.HashByte
-	TraceIdFrom         utils.HashByte16
-	CalledId            utils.HashByte
-	InstanceIdFrom      utils.HashByte
-	AppIdFrom           utils.HashByte
-	SpanIdFrom          utils.HashByte
+	AssumedAppId        HashByte
+	SpanId              HashByte
+	TraceIdFrom         HashByte16
+	CalledId            HashByte
+	InstanceIdFrom      HashByte
+	AppIdFrom           HashByte
+	SpanIdFrom          HashByte
 }
 
 type SocketDataBufferddd struct {
@@ -716,28 +713,25 @@ func ipPort(ip [16]byte, port uint16) netaddr.IPPort {
 	return netaddr.IPPortFrom(i, port)
 }
 
-func (t *Tracer) InitKProcInfo(pid uint32, insID utils.ID, codeType uint16) error {
+func (t *Tracer) InitKProcInfo(pid uint32, appInfo *AppInfo) error {
 	var err error
-	info := tracer.EbpfProcInfo{}
-	info.InstanceId = insID.HashtVal
-	// 获取内存地址
-	//allocDetails, err := tracer.Allocate(int(pid))
-	//if err == nil && allocDetails != nil {
-	//	info.StartAddr = allocDetails.StartAddr
-	//	info.EndAddr = allocDetails.EndAddr
-	//}
-
-	info.CodeType = codeType
-	//klog.Infoln("Major:", major)
-	//klog.Infoln("Minor:", minor)
-	//klog.Infoln("Revision:", revision)
-	//klog.Infoln("goVersion", goVersion)
-	//klog.Infoln("info.StartAddr", info.StartAddr)
-	//klog.Infoln("info.EndAddr", info.EndAddr)
+	var info EbpfProcInfo
+	if appInfo.EBPFProcInfo == nil {
+		info = EbpfProcInfo{
+			InstanceId: appInfo.InstanceIdHash.HashtVal,
+			AppId:      appInfo.AppIdHash.HashtVal,
+			CodeType:   uint16(appInfo.CodeType),
+		}
+	} else {
+		info = *appInfo.EBPFProcInfo
+		info.AppId = appInfo.AppIdHash.HashtVal
+	}
+
 	_, err = tracer.UpdateProcInfoToMap(t.collection, pid, info)
 	if err != nil {
 		klog.Error("failed to update program info", err)
 	}
+	appInfo.EBPFProcInfo = &info
 	return err
 }
 

+ 1 - 1
ebpftracer/tracer/allocate.go

@@ -4,7 +4,7 @@ import (
 	"fmt"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer/ptrace"
 	"github.com/coroot/coroot-node-agent/utils"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 	"math"
 	"os"
 	"runtime"

+ 4 - 3
ebpftracer/tracer/btf_vmlinux.go

@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/btf"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
 	"runtime"
 )
 
@@ -115,12 +116,12 @@ func test(collectionSpec *ebpf.CollectionSpec, opts *ebpf.CollectionOptions) {
 		if err != nil {
 			fmt.Println("Failed to create map:", err)
 		}
-		data := &testStruct{
-			test_id: 123123123,
+		data := &TestStruct{
+			Test_id: 123123123,
 		}
 		// Convert test_t to slice
 		numCPU := runtime.NumCPU()
-		value := make([]testStruct, numCPU)
+		value := make([]TestStruct, numCPU)
 		for i := range value {
 			value[i] = *data
 		}

+ 6 - 8
ebpftracer/tracer/filter.go

@@ -1,23 +1,21 @@
 package tracer
 
 import (
-	"fmt"
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/asm"
-	"os"
-	"strconv"
+	"github.com/coroot/coroot-node-agent/common"
 )
 
 func PidFilter(collectionSpec *ebpf.CollectionSpec) {
-	ENV_PID := os.Getenv("FILTER_PID")
-	if ENV_PID != "" {
-		filterPid, _ := strconv.ParseInt(ENV_PID, 10, 64)
+	filterPid, ok := common.GetFilterPid()
+	if ok {
 		type Editor struct {
 			instructions     *asm.Instructions
 			ReferenceOffsets map[string][]int
 		}
 		for _, prog := range collectionSpec.Programs {
-			fmt.Println("collectionSpec.Program:", prog.Name, prog.SectionName, prog.Type)
+			//klog.Infof("[%v] [%v] [%v]", prog.Type, prog.Name, prog.SectionName)
+			//fmt.Println("collectionSpec.Program:", prog.Name, prog.SectionName, prog.Type)
 			insns := &prog.Instructions
 			refs := insns.ReferenceOffsets()
 
@@ -34,7 +32,7 @@ func PidFilter(collectionSpec *ebpf.CollectionSpec) {
 					continue
 					//return errors.Errorf("symbol %v: load: found %v instead of %v", symbol, load.OpCode, ldDWImm)
 				}
-				load.Constant = filterPid
+				load.Constant = int64(filterPid)
 			}
 		}
 	}

+ 9 - 7
ebpftracer/tracer/inject/inject_linux_amd64.go

@@ -13,6 +13,7 @@ import (
 	"bufio"
 	"debug/elf"
 	"fmt"
+	klog "github.com/sirupsen/logrus"
 	"golang.org/x/arch/x86/x86asm"
 	"os"
 	"strings"
@@ -106,7 +107,7 @@ func (j *JvmInjector) findReleaseAddressInfoFromMem() error {
 		if pc == 0 && inst.Op == x86asm.JMP {
 			// 已经被修改过的首指令
 			j.PreCheck.EbpfCanInjection = true
-			fmt.Printf("Inst already modified. <%s>\n", x86asm.IntelSyntax(inst, 0, nil))
+			klog.Infof("[inject] Inst already modified. <%s>", x86asm.IntelSyntax(inst, 0, nil))
 			return nil
 		}
 		if pc == 0 {
@@ -463,7 +464,7 @@ func (j *JvmInjector) findLibBaseFromProcMaps(libName string) (uint64, string, e
 			if len(fields) > 5 {
 				path := fields[5]
 				if strings.HasSuffix(path, ".so") {
-					fmt.Printf("Found library %s\n", path)
+					klog.Infof("[inject] found library %s", path)
 					return start, path, nil
 				}
 			}
@@ -545,7 +546,7 @@ func (j *JvmInjector) findReleaseFuncContextFromLibPath() error {
 	if err != nil {
 		return fmt.Errorf("Error finding base addresses: %v", err)
 	}
-	fmt.Printf("Base address of (%s)%s: %x\n", "", libName, baseAddress)
+	klog.Infof("[inject] Base address of %s: %x", libName, baseAddress)
 
 	// 获取函数的偏移量
 	functionSym, err := j.getFunctionOffset(libPath, functionName)
@@ -554,9 +555,10 @@ func (j *JvmInjector) findReleaseFuncContextFromLibPath() error {
 	j.ReleaseLibNetInfo.FuncSymbol.SymAddr = baseAddress + functionSym.Value
 	j.ReleaseLibNetInfo.FuncSymbol.SymSize = functionSym.Size
 	if err != nil {
-		return fmt.Errorf("Error getting function offset: %v", err)
+		klog.WithError(err).Errorf("Error getting function offset")
+		return err
 	}
-	fmt.Printf("Actual memory address of %s at base 0x%x: 0x%x\n", functionName, baseAddress, j.ReleaseLibNetInfo.FuncSymbol.SymAddr)
+	klog.Infof("[inject] Actual memory address of %s at base 0x%x: 0x%x", functionName, baseAddress, j.ReleaseLibNetInfo.FuncSymbol.SymAddr)
 	err = j.findReleaseAddressInfoFromMem()
 
 	if err != nil {
@@ -772,12 +774,12 @@ func JvmInject(jvmInjector *JvmInjector) error {
 	// Debug版本无需修改寄存器
 	// 已经加载so并指令修改正确的
 	if jvmInjector.PreCheck.EbpfCanInjection {
-		fmt.Println("eBPF can injection.")
+		klog.Infoln("[inject] eBPF can injection.")
 		return nil
 	}
 
 	if err != nil {
-		//log.Fatalf("Error message during release phase: %v", err)
+		klog.WithError(err).Errorf("[inject] Error message during release phase.")
 		return err
 	}
 

+ 10 - 8
ebpftracer/tracer/offset.go

@@ -6,6 +6,8 @@ import (
 	"errors"
 	"fmt"
 	"github.com/cilium/ebpf"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
+	klog "github.com/sirupsen/logrus"
 	"io"
 	"net"
 	"os"
@@ -33,7 +35,7 @@ type ID struct {
 
 func kernelOffsetInferServer(listener net.Listener) error {
 
-	fmt.Println("[eBPF Kernel Adapt] kernel_offset_infer_server started.")
+	klog.Info("[eBPF Kernel Adapt] kernel_offset_infer_server started.")
 
 	//cpuOnlineCount := runtime.NumCPU()
 
@@ -41,7 +43,7 @@ func kernelOffsetInferServer(listener net.Listener) error {
 		for {
 			conn, err := listener.Accept()
 			if err != nil {
-				fmt.Printf("[eBPF Kernel Adapt] Fail to accept client request: %v\n", err)
+				klog.Errorf("[eBPF Kernel Adapt] Fail to accept client request: %v", err)
 				return
 			}
 			go handleConnection(conn)
@@ -59,10 +61,10 @@ func handleConnection(conn net.Conn) {
 		n, err := conn.Read(buffer)
 		if err != nil {
 			if err == io.EOF {
-				fmt.Printf("[eBPF Kernel Adapt] client connection closed: %v\n", err)
+				klog.Errorf("[eBPF Kernel Adapt] client connection closed: %v", err)
 				return
 			}
-			fmt.Printf("[eBPF Kernel Adapt] Error reading from connection: %v\n", err)
+			klog.Errorf("[eBPF Kernel Adapt] Error reading from connection: %v", err)
 			return
 		}
 		//if n == 0 {
@@ -75,7 +77,7 @@ func handleConnection(conn net.Conn) {
 		if request == "hello" {
 			_, err := conn.Write([]byte("OK"))
 			if err != nil {
-				fmt.Printf("[eBPF Kernel Adapt] Error writing response: %v\n", err)
+				klog.Errorf("[eBPF Kernel Adapt] Error writing response: %v", err)
 				break
 			}
 		}
@@ -89,7 +91,7 @@ func kernelOffsetInferClient() error {
 	}
 	defer conn.Close()
 
-	fmt.Println("[eBPF Kernel Adapt] kernel_offset_infer_client started.")
+	klog.Infoln("[eBPF Kernel Adapt] kernel_offset_infer_client started.")
 
 	request := "hello"
 	_, err = conn.Write([]byte(request))
@@ -109,14 +111,14 @@ func kernelOffsetInferClient() error {
 		}
 
 		response := strings.TrimSpace(string(buffer[:n]))
-		fmt.Println(response)
+		klog.Infoln(response)
 
 		if response == "OK" {
 			break
 		}
 	}
 
-	fmt.Println("[eBPF Kernel Adapt] kernel_offset_infer_client finished.")
+	klog.Infoln("[eBPF Kernel Adapt] kernel_offset_infer_client finished.")
 	return nil
 }
 

+ 2 - 2
ebpftracer/tracer/ptrace/ptrace_linux.go

@@ -17,7 +17,7 @@ package ptrace
 import (
 	"fmt"
 	"github.com/coroot/coroot-node-agent/utils"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 	"os"
 	"strconv"
 	"strings"
@@ -133,7 +133,7 @@ func NewTracedProgram(pid int) (*TracedProgram, error) {
 				return nil, errors.WithStack(err)
 			}
 
-			klog.Info("attach successfully", "tid", tid)
+			klog.Infof("attach successfully %d", tid)
 			tids[tid] = true
 			tidMap[tid] = true
 		}

+ 34 - 34
ebpftracer/tracer/socket.go

@@ -5,7 +5,8 @@ import (
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/btf"
 	"github.com/coroot/coroot-node-agent/utils"
-	"k8s.io/klog/v2"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
+	klog "github.com/sirupsen/logrus"
 	"net"
 	"os"
 	"runtime"
@@ -134,12 +135,12 @@ func Offset() {
 func set_offset_map(collectionSpec *ebpf.CollectionSpec, opts *ebpf.CollectionOptions) {
 	// 解析BTF数据
 	if update_offset_map_from_btf_vmlinux(collectionSpec, opts) != ETR_OK {
-		klog.Infof("[eBPF Kernel Adapt] Set offsets map from btf_vmlinux, not support.\n")
+		klog.Infof("[eBPF Kernel Adapt] Set offsets map from btf_vmlinux, not support.")
 		if update_offset_map_default(collectionSpec, opts) != ETR_OK {
-			klog.Infof("Fatal error, failed to update default offset\n")
+			klog.Infof("Fatal error, failed to update default offset")
 		}
 	} else {
-		klog.Infof("[eBPF Kernel Adapt] Set offsets map from btf_vmlinux, success.\n")
+		klog.Infof("[eBPF Kernel Adapt] Set offsets map from btf_vmlinux, success.")
 	}
 }
 
@@ -151,9 +152,8 @@ func set_offset_map(collectionSpec *ebpf.CollectionSpec, opts *ebpf.CollectionOp
 // __u64 io_event_collect_mode;
 // __u64 io_event_minimal_duration;
 func set_conf_map_default(collectionSpec *ebpf.CollectionSpec, opts *ebpf.CollectionOptions) {
-	fmt.Println("GetHostID")
 	_, charHostID := utils.GetHostID()
-	_, charAppID := utils.GetAppID()
+	//_, charAppID := utils.GetAppID()
 
 	uidBase := uint64(time.Now().UnixNano()/int64(time.Millisecond)) & 0xffffffffffffff
 	numCPU := runtime.NumCPU()
@@ -161,7 +161,7 @@ func set_conf_map_default(collectionSpec *ebpf.CollectionSpec, opts *ebpf.Collec
 	tConf := make([]any, numCPU)
 	for i := range tConf {
 		socketID := uint64(i)<<56 | uint64(uidBase)
-		tracerConf := traceConf{
+		tracerConf := EbpfTraceConf{
 			SocketID:               socketID,
 			CoroutineTraceID:       socketID,
 			ThreadTraceID:          socketID,
@@ -170,15 +170,15 @@ func set_conf_map_default(collectionSpec *ebpf.CollectionSpec, opts *ebpf.Collec
 			IOEventCollectMode:     1,
 			IOEventMinimalDuartion: 1000000,
 			HostID:                 charHostID,
-			APPID:                  charAppID,
-			TotalCpus:              uint64(nCPU),
+			//APPID:                  charAppID,
+			TotalCpus: uint64(nCPU),
 		}
 		tConf[i] = tracerConf
 	}
 	if bpf_table_pre_set_value(collectionSpec, opts, MAP_TRACE_CONF_NAME, tConf) != ETR_OK {
-		klog.Infof("[eBPF Kernel Adapt] Set config map from btf_vmlinux, not support.\n")
+		klog.Infof("[eBPF Kernel Adapt] Set config map from btf_vmlinux, not support.")
 	} else {
-		klog.Infof("[eBPF Kernel Adapt] Set config map from btf_vmlinux, success.\n")
+		klog.Infof("[eBPF Kernel Adapt] Set config map from btf_vmlinux, success.")
 	}
 }
 
@@ -231,26 +231,26 @@ func update_offset_map_from_btf_vmlinux(collectionSpec *ebpf.CollectionSpec, opt
 	struct_sock_skc_state_offset := kernel_struct_field_offset(btfSpec, "sock_common", "skc_state")
 	struct_sock_common_ipv6only_offset := kernel_struct_field_offset(btfSpec, "sock_common", "skc_flags")
 
-	klog.Infof("Offsets from BTF vmlinux:\n")
-	klog.Infof("    copied_seq_offs: 0x%x\n", copied_seq_offs)
-	klog.Infof("    write_seq_offs: 0x%x\n", write_seq_offs)
-	klog.Infof("    files_offs: 0x%x\n", files_offs)
-	klog.Infof("    sk_flags_offs: 0x%x\n", sk_flags_offs)
-	klog.Infof("    struct_files_struct_fdt_offset: 0x%x\n", struct_files_struct_fdt_offset)
-	klog.Infof("    struct_files_private_data_offset: 0x%x\n", struct_files_private_data_offset)
-	klog.Infof("    struct_file_f_inode_offset: 0x%x\n", struct_file_f_inode_offset)
-	klog.Infof("    struct_inode_i_mode_offset: 0x%x\n", struct_inode_i_mode_offset)
-	klog.Infof("    struct_file_dentry_offset: 0x%x\n", struct_file_dentry_offset)
-	klog.Infof("    struct_dentry_name_offset: 0x%x\n", struct_dentry_name_offset)
-	klog.Infof("    struct_sock_family_offset: 0x%x\n", struct_sock_family_offset)
-	klog.Infof("    struct_sock_saddr_offset: 0x%x\n", struct_sock_saddr_offset)
-	klog.Infof("    struct_sock_daddr_offset: 0x%x\n", struct_sock_daddr_offset)
-	klog.Infof("    struct_sock_ip6saddr_offset: 0x%x\n", struct_sock_ip6saddr_offset)
-	klog.Infof("    struct_sock_ip6daddr_offset: 0x%x\n", struct_sock_ip6daddr_offset)
-	klog.Infof("    struct_sock_dport_offset: 0x%x\n", struct_sock_dport_offset)
-	klog.Infof("    struct_sock_sport_offset: 0x%x\n", struct_sock_sport_offset)
-	klog.Infof("    struct_sock_skc_state_offset: 0x%x\n", struct_sock_skc_state_offset)
-	klog.Infof("    struct_sock_common_ipv6only_offset: 0x%x\n", struct_sock_common_ipv6only_offset)
+	klog.Infof("Offsets from BTF vmlinux:")
+	klog.Infof("    copied_seq_offs: 0x%x", copied_seq_offs)
+	klog.Infof("    write_seq_offs: 0x%x", write_seq_offs)
+	klog.Infof("    files_offs: 0x%x", files_offs)
+	klog.Infof("    sk_flags_offs: 0x%x", sk_flags_offs)
+	klog.Infof("    struct_files_struct_fdt_offset: 0x%x", struct_files_struct_fdt_offset)
+	klog.Infof("    struct_files_private_data_offset: 0x%x", struct_files_private_data_offset)
+	klog.Infof("    struct_file_f_inode_offset: 0x%x", struct_file_f_inode_offset)
+	klog.Infof("    struct_inode_i_mode_offset: 0x%x", struct_inode_i_mode_offset)
+	klog.Infof("    struct_file_dentry_offset: 0x%x", struct_file_dentry_offset)
+	klog.Infof("    struct_dentry_name_offset: 0x%x", struct_dentry_name_offset)
+	klog.Infof("    struct_sock_family_offset: 0x%x", struct_sock_family_offset)
+	klog.Infof("    struct_sock_saddr_offset: 0x%x", struct_sock_saddr_offset)
+	klog.Infof("    struct_sock_daddr_offset: 0x%x", struct_sock_daddr_offset)
+	klog.Infof("    struct_sock_ip6saddr_offset: 0x%x", struct_sock_ip6saddr_offset)
+	klog.Infof("    struct_sock_ip6daddr_offset: 0x%x", struct_sock_ip6daddr_offset)
+	klog.Infof("    struct_sock_dport_offset: 0x%x", struct_sock_dport_offset)
+	klog.Infof("    struct_sock_sport_offset: 0x%x", struct_sock_sport_offset)
+	klog.Infof("    struct_sock_skc_state_offset: 0x%x", struct_sock_skc_state_offset)
+	klog.Infof("    struct_sock_common_ipv6only_offset: 0x%x", struct_sock_common_ipv6only_offset)
 
 	if copied_seq_offs < 0 || write_seq_offs < 0 || files_offs < 0 ||
 		sk_flags_offs < 0 || struct_files_struct_fdt_offset < 0 ||
@@ -266,7 +266,7 @@ func update_offset_map_from_btf_vmlinux(collectionSpec *ebpf.CollectionSpec, opt
 		return ETR_NOTSUPP
 	}
 
-	offset := bpfOffsetParam{}
+	offset := BpfOffsetParam{}
 	offset.Ready = 1
 	offset.TaskFilesOffset = uint32(files_offs)
 	offset.SockFlagsOffset = uint32(sk_flags_offs)
@@ -295,7 +295,7 @@ func update_offset_map_from_btf_vmlinux(collectionSpec *ebpf.CollectionSpec, opt
 }
 
 func update_offset_map_default(collectionSpec *ebpf.CollectionSpec, opts *ebpf.CollectionOptions) int {
-	offset := bpfOffsetParam{}
+	offset := BpfOffsetParam{}
 
 	offset.StructFilesStructFdtOffset = 0x20
 	offset.StructFilesPrivateDataOffset = 0xc8
@@ -319,7 +319,7 @@ func update_offset_map_default(collectionSpec *ebpf.CollectionSpec, opts *ebpf.C
 	return ETR_OK
 }
 
-func update_offsets_table(collectionSpec *ebpf.CollectionSpec, opts *ebpf.CollectionOptions, offset bpfOffsetParam) int {
+func update_offsets_table(collectionSpec *ebpf.CollectionSpec, opts *ebpf.CollectionOptions, offset BpfOffsetParam) int {
 	numCPU := runtime.NumCPU()
 	offsetData := make([]any, numCPU)
 	for i := range offsetData {

+ 57 - 3
flags/flags.go

@@ -1,10 +1,15 @@
 package flags
 
 import (
+	"encoding/json"
+	"fmt"
+	"github.com/coroot/coroot-node-agent/utils"
+	"github.com/coroot/coroot-node-agent/utils/modelse"
+	"github.com/jedib0t/go-pretty/v6/table"
+	"gopkg.in/alecthomas/kingpin.v2"
 	"os"
+	"path"
 	"strings"
-
-	"gopkg.in/alecthomas/kingpin.v2"
 )
 
 var (
@@ -31,11 +36,14 @@ var (
 	ApiKey            = kingpin.Flag("api-key", "Coroot API key").Envar("API_KEY").String()
 	MetricsEndpoint   = kingpin.Flag("metrics-endpoint", "The URL of the endpoint to send metrics to").Envar("METRICS_ENDPOINT").URL()
 	TracesEndpoint    = kingpin.Flag("traces-endpoint", "The URL of the endpoint to send traces to").Envar("TRACES_ENDPOINT").URL()
+	ConfigEndpoint    = kingpin.Flag("config-endpoint", "The URL of the endpoint to send traces to").Envar("CONFIG_ENDPOINT").Default("10.0.16.250:18080").String()
 	LogsEndpoint      = kingpin.Flag("logs-endpoint", "The URL of the endpoint to send logs to").Envar("LOGS_ENDPOINT").URL()
 	ProfilesEndpoint  = kingpin.Flag("profiles-endpoint", "The URL of the endpoint to send profiles to").Envar("PROFILES_ENDPOINT").URL()
 
 	ScrapeInterval = kingpin.Flag("scrape-interval", "How often to gather metrics from the agent").Default("15s").Envar("SCRAPE_INTERVAL").Duration()
 	WalDir         = kingpin.Flag("wal-dir", "Path to where the agent stores data (e.g. the metrics Write-Ahead Log)").Default("/tmp/coroot-node-agent").Envar("WAL_DIR").String()
+	DumpApps       = kingpin.Flag("dump", "Dump app snap").Default("false").Bool()
+	LogLevel       = kingpin.Flag("log-level", "Log level").Envar("LOG_LEVEL").Default("info").String()
 )
 
 func GetString(fl *string) string {
@@ -52,7 +60,9 @@ func init() {
 
 	kingpin.HelpFlag.Short('h').Hidden()
 	kingpin.Parse()
-
+	if *DumpApps {
+		DumpTableFeatures()
+	}
 	if *CollectorEndpoint != nil {
 		u := *CollectorEndpoint
 		if *MetricsEndpoint == nil {
@@ -73,3 +83,47 @@ func init() {
 		*ListenAddress = "127.0.0.1:10300"
 	}
 }
+
+func DumpTableFeatures() {
+	dumpPath := path.Join(utils.GetDefaultLogPath(), "memdump")
+	fileName := path.Join(dumpPath, "app.snap")
+	content, err := os.ReadFile(fileName)
+	if err != nil {
+		fmt.Println(err.Error())
+	}
+
+	s := make(map[uint32]modelse.AppStatusInfo)
+	err = json.Unmarshal(content, &s)
+	if err != nil {
+		fmt.Println(err.Error())
+	}
+	t := table.NewWriter()
+	for pid, info := range s {
+		service := fmt.Sprintf("%s:%d", info.Sn, info.Sport)
+		t.AppendRow(table.Row{
+			pid,
+			info.ProcName,
+			info.AppName,
+			info.Language,
+			service,
+			info.AppID,
+			//info.AgentID,
+			info.RegisterAt,
+			info.UpdateAt,
+		})
+	}
+	t.SetAutoIndex(true)
+	t.AppendHeader(table.Row{
+		"pid",
+		"process",
+		"app name",
+		"code",
+		"service",
+		"app id",
+		//"agent id",
+		"register at",
+		"update at",
+	})
+	fmt.Println(t.Render())
+	os.Exit(0)
+}

+ 13 - 5
go.mod

@@ -14,15 +14,23 @@ require (
 	github.com/docker/docker v25.0.0+incompatible
 	github.com/florianl/go-conntrack v0.3.0
 	github.com/go-kit/log v0.2.1
+	github.com/go-logr/logr v1.4.1
+	github.com/go-sql-driver/mysql v1.8.1
+	github.com/gomodule/redigo v1.9.2
 	github.com/grafana/pyroscope/ebpf v0.4.1
+	github.com/hashicorp/go-version v1.6.0
+	github.com/jedib0t/go-pretty/v6 v6.6.0
 	github.com/jpillora/backoff v1.0.0
 	github.com/mdlayher/taskstats v0.0.0-20230712191918-387b3d561d14
+	github.com/natefinch/lumberjack v0.0.0
 	github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417
+	github.com/pkg/errors v0.9.1
 	github.com/prometheus/client_golang v1.18.0
 	github.com/prometheus/client_model v0.5.0
 	github.com/prometheus/common v0.46.0
 	github.com/prometheus/prometheus v0.50.1
 	github.com/pyroscope-io/dotnetdiag v1.2.1
+	github.com/sirupsen/logrus v1.9.3
 	github.com/stretchr/testify v1.8.4
 	github.com/vishvananda/netlink v1.2.1-beta.2.0.20220608195807-1a118fe229fc
 	github.com/vishvananda/netns v0.0.4
@@ -41,11 +49,11 @@ require (
 	gopkg.in/alecthomas/kingpin.v2 v2.2.6
 	gopkg.in/yaml.v2 v2.4.0
 	inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a
-	k8s.io/klog/v2 v2.120.1
 )
 
 require (
 	cloud.google.com/go/compute v1.23.3 // indirect
+	filippo.io/edwards25519 v1.1.0 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 // indirect
@@ -77,7 +85,6 @@ require (
 	github.com/felixge/httpsnoop v1.0.4 // indirect
 	github.com/fsnotify/fsnotify v1.7.0 // indirect
 	github.com/go-logfmt/logfmt v0.6.0 // indirect
-	github.com/go-logr/logr v1.4.1 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-ole/go-ole v1.2.6 // indirect
 	github.com/go-openapi/analysis v0.21.4 // indirect
@@ -102,7 +109,6 @@ require (
 	github.com/google/uuid v1.5.0 // indirect
 	github.com/grafana/regexp v0.0.0-20221123153739-15dc172cd2db // indirect
 	github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
-	github.com/hashicorp/go-version v1.6.0 // indirect
 	github.com/hashicorp/golang-lru/v2 v2.0.5 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab // indirect
@@ -116,6 +122,7 @@ require (
 	github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
 	github.com/magiconair/properties v1.8.6 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
+	github.com/mattn/go-runewidth v0.0.15 // indirect
 	github.com/mdlayher/genetlink v1.3.2 // indirect
 	github.com/mdlayher/netlink v1.7.2 // indirect
 	github.com/mdlayher/socket v0.4.1 // indirect
@@ -138,15 +145,14 @@ require (
 	github.com/pelletier/go-toml/v2 v2.0.5 // indirect
 	github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
 	github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
-	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
 	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
 	github.com/prometheus/common/sigv4 v0.1.0 // indirect
 	github.com/prometheus/procfs v0.12.0 // indirect
+	github.com/rivo/uniseg v0.2.0 // indirect
 	github.com/samber/lo v1.38.1 // indirect
 	github.com/sasha-s/go-deadlock v0.3.1 // indirect
 	github.com/shirou/gopsutil/v3 v3.22.10 // indirect
-	github.com/sirupsen/logrus v1.9.3 // indirect
 	github.com/spf13/afero v1.9.2 // indirect
 	github.com/spf13/cast v1.5.0 // indirect
 	github.com/spf13/cobra v1.6.1 // indirect
@@ -182,10 +188,12 @@ require (
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	k8s.io/apimachinery v0.28.6 // indirect
 	k8s.io/client-go v0.28.6 // indirect
+	k8s.io/klog/v2 v2.120.1 // indirect
 	k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
 )
 
 replace (
+	github.com/natefinch/lumberjack => ./pkg/lumberjack
 	github.com/optiopay/kafka => github.com/cilium/kafka v0.0.0-20180809090225-01ce283b732b
 	github.com/pyroscope-io/dotnetdiag => github.com/coroot/dotnetdiag v1.2.2
 	go.opentelemetry.io/otel => ./pkg/go.opentelemetry.io/otel

+ 16 - 0
go.sum

@@ -41,6 +41,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
 cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
 cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
 github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
 github.com/Azure/azure-sdk-for-go v67.1.0+incompatible h1:oziYcaopbnIKfM69DL05wXdypiqfrUKdxUKrKpynJTw=
 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ=
@@ -69,6 +71,8 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp
 github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
 github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
+github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/Code-Hex/go-generics-cache v1.3.1 h1:i8rLwyhoyhaerr7JpjtYjJZUcCbWOdiYO3fZXLiEC4g=
 github.com/Code-Hex/go-generics-cache v1.3.1/go.mod h1:qxcC9kRVrct9rHeiYpFWSoW1vxyillCVzX13KZG8dl4=
@@ -461,6 +465,8 @@ github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9F
 github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg=
 github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8=
 github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
+github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
+github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg=
 github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
@@ -546,6 +552,8 @@ github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
 github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/gomodule/redigo v1.9.2 h1:HrutZBLhSIU8abiSfW8pj8mPhOyMYjZT/wcA4/L9L9s=
+github.com/gomodule/redigo v1.9.2/go.mod h1:KsU3hiK/Ay8U42qpaJk+kuNa3C+spxapWpM+ywhcgtw=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
@@ -678,6 +686,8 @@ github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLf
 github.com/ionos-cloud/sdk-go/v6 v6.1.11 h1:J/uRN4UWO3wCyGOeDdMKv8LWRzKu6UIkLEaes38Kzh8=
 github.com/ionos-cloud/sdk-go/v6 v6.1.11/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k=
 github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
+github.com/jedib0t/go-pretty/v6 v6.6.0 h1:wmZVuAcEkZRT+Aq1xXpE8IGat4vE5WXOMmBpbQqERXw=
+github.com/jedib0t/go-pretty/v6 v6.6.0/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E=
 github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
@@ -768,6 +778,8 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
 github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
 github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
+github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
+github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -961,6 +973,8 @@ github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3c
 github.com/prometheus/prometheus v0.50.1 h1:N2L+DYrxqPh4WZStU+o1p/gQlBaqFbcLBTjlp3vpdXw=
 github.com/prometheus/prometheus v0.50.1/go.mod h1:FvE8dtQ1Ww63IlyKBn1V4s+zMwF9kHkVNkQBR1pM4CU=
 github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
 github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@@ -1624,6 +1638,8 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
 gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=

+ 1 - 1
logs/journald_reader.go

@@ -8,7 +8,7 @@ import (
 
 	"github.com/coreos/go-systemd/v22/sdjournal"
 	"github.com/coroot/logparser"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 type JournaldReader struct {

+ 181 - 0
logs/log.go

@@ -0,0 +1,181 @@
+package logs
+
+import (
+	"fmt"
+	"github.com/natefinch/lumberjack"
+	"github.com/sirupsen/logrus"
+	"log"
+	"os"
+	"path"
+	"path/filepath"
+	"strings"
+	"time"
+)
+
+const (
+	defaultTimestamp    = "2006-01-02 15:04:05.999"
+	defaultLogDelimeter = "|"
+	defaultLogLevel     = logrus.ErrorLevel
+	defaultLogFileSize  = 50
+	// 日志保存天数
+	defaultMaxAge = 3
+	// 每天最多保存的日志数量
+	defaultMaxBackups = 3
+)
+
+type LogConfig struct {
+	Path       string
+	AppInfo    string
+	MaxSize    int // 日志文件最大尺寸,单位MB
+	MaxBackups int // 最多保留的旧日志文件数
+	MaxAge     int // 日志文件保留的最长时间,单位天
+	// 控制台输出
+	Console bool
+}
+
+// 定义Hook结构体
+type FileHook struct {
+	level     logrus.Level
+	formatter logrus.Formatter
+	logger    *log.Logger
+}
+
+func InitLog(level string, config LogConfig) error {
+	FormatterInit()
+	if config.Console {
+		logrus.SetOutput(os.Stdout)
+	} else {
+		logrus.SetOutput(&NoOut{})
+	}
+	if level == "" {
+		level = "debug"
+	}
+	logrusLevel, err := logrus.ParseLevel(level)
+	if err == nil {
+		logrus.SetLevel(logrusLevel)
+	} else {
+		logrus.SetLevel(defaultLogLevel)
+		logrus.WithError(err).Error("log level parse error, use default log level error")
+	}
+	logrus.SetFormatter(NewFormatter())
+	logrus.SetReportCaller(true)
+
+	if config.Path == "" {
+		config.Path = log_config.Path
+	}
+
+	if config.Path == "" {
+		return fmt.Errorf("config.Path is nil")
+	}
+
+	log_config = config
+	RegisterLogHook(config)
+	return nil
+}
+
+// 实现Hook接口
+func (hook *FileHook) Levels() []logrus.Level {
+	return logrus.AllLevels[:hook.level+1]
+}
+
+func (hook *FileHook) Fire(entry *logrus.Entry) error {
+	// 将日志消息格式化为字符串
+	bytes, err := hook.formatter.Format(entry)
+	hook.logger.Print(string(bytes))
+	if err != nil {
+		return fmt.Errorf("failed to format log entry: %v", err)
+	}
+	if err != nil {
+		return fmt.Errorf("failed to write log entry: %v", err)
+	}
+	return nil
+}
+
+type NoOut struct {
+}
+
+func (no *NoOut) Write(p []byte) (n int, err error) {
+	return 0, nil
+}
+
+func RegisterLogHook(logConf LogConfig) {
+	hooks := make(logrus.LevelHooks)
+	logrus.StandardLogger().ReplaceHooks(hooks)
+	// 日志文件最大尺寸,不超过100MB
+	if logConf.MaxSize > defaultLogFileSize || logConf.MaxSize < 1 {
+		logConf.MaxSize = defaultLogFileSize
+	}
+	// 最多保留的旧日志文件数
+	if logConf.MaxBackups > defaultMaxBackups || logConf.MaxBackups == 0 {
+		logConf.MaxBackups = defaultMaxBackups
+	}
+	// 日志文件保留的最长时间,单位天
+	if logConf.MaxAge > defaultMaxAge || logConf.MaxAge == 0 {
+		logConf.MaxAge = defaultMaxAge
+	}
+
+	for _, val := range logrus.AllLevels {
+		if val <= logrus.GetLevel() {
+			fileName := filepath.Join(logConf.Path, logConf.AppInfo+"."+val.String()+".log")
+			archive := &lumberjack.Logger{
+				Filename:   fileName,
+				MaxSize:    logConf.MaxSize,    // 日志文件最大尺寸,单位MB
+				MaxBackups: logConf.MaxBackups, // 最多保留的旧日志文件数
+				MaxAge:     logConf.MaxAge,     // 日志文件保留的最长时间,单位天
+				LocalTime:  true,               // 使用本地时间
+				Compress:   true,
+			}
+			sysLogger := &log.Logger{}
+			sysLogger.SetOutput(archive)
+			hooker := &FileHook{
+				level:     val,
+				formatter: NewFormatter(),
+				logger:    sysLogger,
+			}
+			hooks.Add(hooker)
+		}
+	}
+}
+
+func FormatterInit() {
+	logrus.SetFormatter(NewFormatter())
+	logrus.SetReportCaller(true)
+}
+
+func NewFormatter() *MyFormatter {
+	return &MyFormatter{
+		Pid:             os.Getpid(),
+		TimestampFormat: defaultTimestamp,
+	}
+}
+
+// Custom log format definition
+type MyFormatter struct {
+	Pid             int
+	TimestampFormat string
+}
+
+func (s *MyFormatter) Format(entry *logrus.Entry) ([]byte, error) {
+	//var callerFun string
+	var fileInfo string
+	var entryDataInfo string
+
+	for key, val := range entry.Data {
+		entryDataInfo += fmt.Sprintf(" %s=\"%v\"", key, val)
+	}
+	if entry.HasCaller() {
+		//callerFun = entry.Caller.Function
+		fileInfo = fmt.Sprintf("%s:%d", path.Base(entry.Caller.File), entry.Caller.Line)
+	}
+
+	timestamp := time.Now().Local().Format(s.TimestampFormat)
+	msg := fmt.Sprintf("%s|%s|%d|%s] %s%s\n",
+		timestamp, strings.ToUpper(entry.Level.String()), s.Pid, fileInfo, entry.Message, entryDataInfo)
+	return []byte(msg), nil
+}
+
+var log_config LogConfig
+
+func GetLogConf() LogConfig {
+	return log_config
+}

+ 1 - 1
logs/otel.go

@@ -12,10 +12,10 @@ import (
 	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/flags"
 	"github.com/coroot/logparser"
+	klog "github.com/sirupsen/logrus"
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/sdk/resource"
 	semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
-	"k8s.io/klog/v2"
 )
 
 var otelLogger otelLogs.Logger

+ 1 - 1
logs/tail_reader.go

@@ -9,7 +9,7 @@ import (
 	"time"
 
 	"github.com/coroot/logparser"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 var (

+ 38 - 19
logs/tail_reader_test.go

@@ -2,6 +2,9 @@ package main
 
 import (
 	"bytes"
+	"github.com/coroot/coroot-node-agent/utils"
+	"github.com/coroot/coroot-node-agent/utils/enums"
+	log "github.com/sirupsen/logrus"
 	"net/http"
 	_ "net/http/pprof"
 	"os"
@@ -24,7 +27,6 @@ import (
 	"golang.org/x/mod/semver"
 	"golang.org/x/sys/unix"
 	"golang.org/x/time/rate"
-	"k8s.io/klog/v2"
 )
 
 var (
@@ -33,6 +35,10 @@ var (
 
 const minSupportedKernelVersion = "4.16"
 
+func init() {
+	logs.FormatterInit()
+}
+
 func uname() (string, string, error) {
 	runtime.LockOSThread()
 	defer runtime.UnlockOSThread()
@@ -70,11 +76,11 @@ func machineID() string {
 	for _, p := range []string{"sys/devices/virtual/dmi/id/product_uuid", "etc/machine-id", "var/lib/dbus/machine-id"} {
 		payload, err := os.ReadFile(path.Join("/proc/1/root", p))
 		if err != nil {
-			klog.Warningln("failed to read machine-id:", err)
+			log.Warningln("failed to read machine-id:", err)
 			continue
 		}
 		id := strings.TrimSpace(strings.Replace(string(payload), "-", "", -1))
-		klog.Infoln("machine-id: ", id)
+		log.Infoln("machine-id: ", id)
 		return id
 	}
 	return ""
@@ -83,7 +89,7 @@ func machineID() string {
 func whitelistNodeExternalNetworks() {
 	netdevs, err := node.NetDevices()
 	if err != nil {
-		klog.Warningln("failed to get network interfaces:", err)
+		log.Warningln("failed to get network interfaces:", err)
 		return
 	}
 	for _, iface := range netdevs {
@@ -108,24 +114,37 @@ type MetricData struct {
 }
 
 func main() {
-	klog.LogToStderr(false)
-	klog.SetOutput(&RateLimitedLogOutput{limiter: rate.NewLimiter(rate.Limit(*flags.LogPerSecond), *flags.LogBurst)})
+	err := logs.InitLog(*flags.LogLevel, logs.LogConfig{
+		Path:       utils.GetDefaultLogPath(),
+		AppInfo:    enums.DaemonProc,
+		MaxSize:    50, // 日志文件最大尺寸,单位MB
+		MaxBackups: 3,  // 最多保留的旧日志文件数
+		MaxAge:     3,  // 日志文件保留的最长时间,单位天
+		Console:    true,
+	})
+
+	if err != nil {
+		log.WithError(err).Errorf("log init error.")
+	}
+
+	//log.LogToStderr(false)
+	//log.SetOutput(&RateLimitedLogOutput{limiter: rate.NewLimiter(rate.Limit(*flags.LogPerSecond), *flags.LogBurst)})
 
-	klog.Infoln("agent version:", version)
+	log.Infoln("agent version:", version)
 
 	hostname, kv, err := uname()
 	if err != nil {
-		klog.Exitln("failed to get uname:", err)
+		log.Fatalln("failed to get uname:", err)
 	}
-	klog.Infoln("hostname:", hostname)
-	klog.Infoln("kernel version:", kv)
+	log.Infoln("hostname:", hostname)
+	log.Infoln("kernel version:", kv)
 
 	ver := common.KernelMajorMinor(kv)
 	if ver == "" {
-		klog.Exitln("invalid kernel version:", kv)
+		log.Fatalln("invalid kernel version:", kv)
 	}
 	if semver.Compare("v"+ver, "v"+minSupportedKernelVersion) == -1 {
-		klog.Exitf("the minimum Linux kernel version required is %s or later", minSupportedKernelVersion)
+		log.Fatalf("the minimum Linux kernel version required is %s or later", minSupportedKernelVersion)
 	}
 
 	whitelistNodeExternalNetworks()
@@ -140,7 +159,7 @@ func main() {
 	registerer.MustRegister(info("node_agent_info", version))
 
 	if err := registerer.Register(node.NewCollector(hostname, kv)); err != nil {
-		klog.Exitln(err)
+		log.Fatalln(err)
 	}
 
 	//processInfoCh := profiling.Init(machineId, hostname)
@@ -148,10 +167,10 @@ func main() {
 	cr, err := containers.NewRegistry(registerer, kv, nil)
 
 	if err != nil {
-		klog.Exitln(err)
+		log.Fatalln(err)
 	}
 	defer cr.Close()
-	klog.Infoln("START_TRACE")
+	log.Infoln("START_TRACE")
 
 	//profiling.Start()
 	//defer profiling.Stop()
@@ -225,13 +244,13 @@ func main() {
 	}
 
 	if err := prom.StartAgent(machineId); err != nil {
-		klog.Exitln(err)
+		log.Fatalln(err)
 	}
 
 	http.Handle("/metrics", promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorLog: logger{}, Registry: registerer}))
 	http.HandleFunc("/metrics2", metricsHandler)
-	klog.Infoln("listening on:", *flags.ListenAddress)
-	klog.Errorln(http.ListenAndServe(*flags.ListenAddress, nil))
+	log.Infoln("listening on:", *flags.ListenAddress)
+	log.Errorln(http.ListenAndServe(*flags.ListenAddress, nil))
 }
 
 func info(name, version string) prometheus.Collector {
@@ -246,7 +265,7 @@ func info(name, version string) prometheus.Collector {
 type logger struct{}
 
 func (l logger) Println(v ...interface{}) {
-	klog.Errorln(v...)
+	log.Errorln(v...)
 }
 
 type RateLimitedLogOutput struct {

+ 1 - 1
node/collector.go

@@ -5,7 +5,7 @@ import (
 	"github.com/coroot/coroot-node-agent/flags"
 	"github.com/coroot/coroot-node-agent/node/metadata"
 	"github.com/prometheus/client_golang/prometheus"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 var (

+ 1 - 1
node/disk.go

@@ -7,7 +7,7 @@ import (
 	"strconv"
 	"strings"
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 var blockDevice = regexp.MustCompile(`^(dm-\d+|(s|h|xv|v)d[a-z]|md\d+|nvme\d+n\d+|rbd\d+)`)

+ 1 - 1
node/memory.go

@@ -6,7 +6,7 @@ import (
 	"strconv"
 	"strings"
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 func memoryInfo(procRoot string) (MemoryStat, error) {

+ 1 - 1
node/metadata/aws.go

@@ -9,7 +9,7 @@ import (
 	"net/http"
 
 	"github.com/coroot/coroot-node-agent/proc"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 const awsInstanceMetadataURL = "http://169.254.169.254/latest"

+ 1 - 1
node/metadata/azure.go

@@ -4,7 +4,7 @@ import (
 	"encoding/json"
 	"net/http"
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 const (

+ 1 - 1
node/metadata/digital_ocean.go

@@ -4,7 +4,7 @@ import (
 	"io"
 	"net/http"
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 const doInstanceMetadataURL = "http://169.254.169.254/metadata/v1/"

+ 1 - 1
node/metadata/gcp.go

@@ -4,7 +4,7 @@ import (
 	"strings"
 
 	gcp "cloud.google.com/go/compute/metadata"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 func getGcpMetadata() *CloudMetadata {

+ 1 - 1
node/metadata/hetzner.go

@@ -3,8 +3,8 @@ package metadata
 import (
 	"net/http"
 
+	klog "github.com/sirupsen/logrus"
 	"gopkg.in/yaml.v2"
-	"k8s.io/klog/v2"
 )
 
 const hetznerInstanceMetadataURL = "http://169.254.169.254/hetzner/v1/metadata"

+ 1 - 1
node/metadata/metadata.go

@@ -7,7 +7,7 @@ import (
 	"strings"
 	"time"
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 const metadataServiceTimeout = 5 * time.Second

+ 1 - 1
pinger/pinger.go

@@ -12,12 +12,12 @@ import (
 	"time"
 
 	"github.com/coroot/coroot-node-agent/proc"
+	klog "github.com/sirupsen/logrus"
 	"github.com/vishvananda/netns"
 	"golang.org/x/net/icmp"
 	"golang.org/x/net/ipv4"
 	"golang.org/x/sys/unix"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 
 const (

+ 5 - 5
pkg/go.opentelemetry.io/otel/example/otel-collector/go.mod

@@ -14,7 +14,7 @@ require (
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0
 	go.opentelemetry.io/otel/sdk v1.22.0
 	go.opentelemetry.io/otel/trace v1.22.0
-	google.golang.org/grpc v1.58.2
+	google.golang.org/grpc v1.61.0
 )
 
 require (
@@ -26,11 +26,11 @@ require (
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
 	go.opentelemetry.io/otel/metric v1.22.0 // indirect
 	go.opentelemetry.io/proto/otlp v1.0.0 // indirect
-	golang.org/x/net v0.12.0 // indirect
+	golang.org/x/net v0.22.0 // indirect
 	golang.org/x/sys v0.18.0 // indirect
-	golang.org/x/text v0.11.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
+	golang.org/x/text v0.14.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
 	google.golang.org/protobuf v1.32.0 // indirect
 )
 

+ 5 - 0
pkg/go.opentelemetry.io/otel/example/otel-collector/go.sum

@@ -22,19 +22,24 @@ go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v8
 go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
 golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
 golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
+golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
 golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
 golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
 golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g=
 google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
 google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
+google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
 google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
 google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
+google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=

+ 187 - 197
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/apm_exporter.go

@@ -4,7 +4,9 @@ import (
 	"crypto/md5"
 	"encoding/json"
 	"fmt"
-	"os"
+	. "github.com/coroot/coroot-node-agent/ebpftracer"
+	"github.com/coroot/coroot-node-agent/ebpftracer/l7"
+	"github.com/coroot/coroot-node-agent/utils"
 	"sort"
 	"strconv"
 	"strings"
@@ -17,25 +19,6 @@ import (
 	tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
 )
 
-const (
-	ProtocolTrace int = 200
-
-	ProtocolHTTP      int = 1
-	ProtocolPostgres  int = 2
-	ProtocolRedis     int = 3
-	ProtocolMemcached int = 4
-	ProtocolMysql     int = 5
-	ProtocolMongo     int = 6
-	ProtocolKafka     int = 7
-	ProtocolCassandra int = 8
-	ProtocolRabbitmq  int = 9
-	ProtocolNats      int = 10
-	ProtocolHTTP2     int = 11
-	ProtocolDubbo2    int = 12
-	ProtocolDNS       int = 13
-	ProtocolDM        int = 14
-)
-
 const (
 	APP_SERVICE_TYPE   = "APPLICATION"
 	SQL_SERVICE_TYPE   = "SQL"
@@ -163,12 +146,9 @@ func tracetransformData(sdl []tracesdk.ReadOnlySpan) map[int][]RootDataT {
 		fmt.Println("------event_num---- "+sd.Name(), "--->", len(sd.Events())) // 一次请求完整数据
 		// 构建map *RootDataT
 		var rootData RootDataT
-		// todo 应用注册逻辑
-		if os.Getenv("JAVA") == "1" {
-			rootData = initRootDataJava()
-		} else {
-			rootData = initRootDataFromEvent()
-		}
+
+		rootData = initRootDataFromEvent()
+
 		// build http入口 MapInfoT
 		code_type := buildAppMapFromEvent(&rootData, sd)
 		// 构建maps
@@ -176,23 +156,20 @@ func tracetransformData(sdl []tracesdk.ReadOnlySpan) map[int][]RootDataT {
 			aaa, _ := json.Marshal(event)
 			fmt.Println("event.info", string(aaa))
 			mNode := buildMapNodeFromEvent(event)
-			switch event.EventType {
+			switch EventType(event.EventType) {
 			// stack
-			case 11:
+			case EventTypeFunEnt:
 			// l7 event
-			case 10:
-				switch event.ProtocolType {
-				// http
-				case ProtocolHTTP:
+			case EventTypeL7Request:
+				switch l7.Protocol(event.ProtocolType) {
+				case l7.ProtocolHTTP:
 					buildHttpMapFromEvent(&mNode, event)
-				// mysql
-				case ProtocolMysql:
+				case l7.ProtocolMysql:
 					buildMysqlMapEvent(&mNode, event)
-				// redis
-				case ProtocolRedis:
+				case l7.ProtocolRedis:
 					buildRedisMapEvent(&mNode, event)
 				// dm
-				case ProtocolDM:
+				case l7.ProtocolDM:
 					buildDMMapEvent(&mNode, event)
 				}
 			}
@@ -367,70 +344,72 @@ func buildLevelFromEvent(sdl *RootDataT) {
 	}
 }
 
-func initRootData(traceId string) RootDataT {
-	data := RootDataT{
-		AccountId:      110,
-		AgentId:        1011005252979954, // TODO 更新 基于 ip:port + process_name + exe路径生成
-		AgentVersion:   "2.1.0",
-		AppId:          5410049101545798, // TODO 更新 基于appname生成
-		AppIdFrom:      -1,
-		AppName:        "eBPF-agent", // TODO 更新 ip:port || process_name
-		CalledId:       -1,
-		ClientIp:       "",
-		CollTime:       0,
-		Cpu:            0,
-		Custom:         "",
-		HostId:         10154813500555812,
-		HostName:       "localhost",
-		HttpCode:       0,
-		HttpMethod:     "",
-		InstanceId:     1005051101515357, // TODO 更新 基于ip:port
-		InstanceIdFrom: -1,
-		Maps:           []MapInfoT{},
-		MemU:           0,
-		MemUP:          0,
-		OperType:       "",
-		Parameters:     []interface{}{},
-		ParentTaskName: 0,
-		Period:         -1,
-		RespTime:       0,
-		Sampling:       0,
-		ServiceName:    "GO",
-		ServiceType:    APP_SERVICE_TYPE,
-		Sip:            "",
-		Sn:             "",
-		SpanIdFrom:     "",
-		Sport:          0,
-		TId:            -1,
-		TName:          "",
-		TraceId:        traceId,
-		TransIds:       []interface{}{},
-		TypeFrom:       "",
-		Uri:            "",
-		UserDir:        0,
-		VipIds:         []interface{}{},
-	}
-	return data
-}
+//func initRootData(traceId string) RootDataT {
+//	data := RootDataT{
+//		AccountId:      110,
+//		AgentId:        1011005252979954, // TODO 更新 基于 ip:port + process_name + exe路径生成
+//		AgentVersion:   "2.1.0",
+//		AppId:          5410049101545798, // TODO 更新 基于appname生成
+//		AppIdFrom:      -1,
+//		AppName:        "eBPF-agent", // TODO 更新 ip:port || process_name
+//		CalledId:       -1,
+//		ClientIp:       "",
+//		CollTime:       0,
+//		Cpu:            0,
+//		Custom:         "",
+//		HostId:         10154813500555812,
+//		HostName:       "localhost",
+//		HttpCode:       0,
+//		HttpMethod:     "",
+//		InstanceId:     1005051101515357, // TODO 更新 基于ip:port
+//		InstanceIdFrom: -1,
+//		Maps:           []MapInfoT{},
+//		MemU:           0,
+//		MemUP:          0,
+//		OperType:       "",
+//		Parameters:     []interface{}{},
+//		ParentTaskName: 0,
+//		Period:         -1,
+//		RespTime:       0,
+//		Sampling:       0,
+//		ServiceName:    "GO",
+//		ServiceType:    APP_SERVICE_TYPE,
+//		Sip:            "",
+//		Sn:             "",
+//		SpanIdFrom:     "",
+//		Sport:          0,
+//		TId:            -1,
+//		TName:          "",
+//		TraceId:        traceId,
+//		TransIds:       []interface{}{},
+//		TypeFrom:       "",
+//		Uri:            "",
+//		UserDir:        0,
+//		VipIds:         []interface{}{},
+//	}
+//	return data
+//}
 
 func initRootDataFromEvent() RootDataT {
+	hostID, _ := utils.GetHostID()
 	data := RootDataT{
+		// todo AccountId
 		AccountId:      110,
-		AgentId:        1011005252979954, // TODO 更新 基于 ip:port + process_name + exe路径生成
+		AgentId:        0, // 基于 ip:port + process_name + exe路径生成
 		AgentVersion:   "2.1.0",
-		AppId:          5410049101545798, // TODO 更新 基于appname生成
+		AppId:          0, // 基于appname生成
 		AppIdFrom:      -1,
-		AppName:        "eBPF-agent", // TODO 更新 ip:port || process_name
+		AppName:        "eBPF-agent", // server配置
 		CalledId:       -1,
 		ClientIp:       "",
 		CollTime:       0,
 		Cpu:            0,
 		Custom:         "",
-		HostId:         10154813500555812,
+		HostId:         hostID,
 		HostName:       "localhost",
 		HttpCode:       0,
 		HttpMethod:     "",
-		InstanceId:     1005051101515357, // TODO 更新 基于ip:port
+		InstanceId:     0, // 基于ip:port
 		InstanceIdFrom: -1,
 		Maps:           []MapInfoT{},
 		MemU:           0,
@@ -441,7 +420,7 @@ func initRootDataFromEvent() RootDataT {
 		Period:         -1,
 		RespTime:       0,
 		Sampling:       0,
-		ServiceName:    "GO",
+		ServiceName:    "",
 		ServiceType:    APP_SERVICE_TYPE,
 		Sip:            "",
 		Sn:             "",
@@ -584,25 +563,25 @@ func buildMapNodeFromEvent(event tracesdk.Event) MapInfoT {
 }
 
 // 构建拼装
-func buildAndAssemblyMap(sd apmTraceSpan, traceRoot *TraceMapT) MapInfoT {
-	mNode, mapType := initMapNode(span(sd))
-	switch mapType {
-	case "APPLICATION":
-		buildAppMap(&mNode, traceRoot, sd)
-		traceRoot.TheEnd = true
-	case "HTTP":
-		buildHttpMap(&mNode, sd)
-	case "Mysql":
-		buildMysqlMap(&mNode, sd)
-	case "Redis":
-		buildRedisMap(&mNode, sd)
-	}
-	if mapType != "" {
-		mNode.Nid = traceRoot.Index
-		traceRoot.RootData.Maps = append(traceRoot.RootData.Maps, mNode)
-	}
-	return mNode
-}
+//func buildAndAssemblyMap(sd apmTraceSpan, traceRoot *TraceMapT) MapInfoT {
+//	mNode, mapType := initMapNode(span(sd))
+//	switch mapType {
+//	case "APPLICATION":
+//		buildAppMap(&mNode, traceRoot, sd)
+//		traceRoot.TheEnd = true
+//	case "HTTP":
+//		buildHttpMap(&mNode, sd)
+//	case "Mysql":
+//		buildMysqlMap(&mNode, sd)
+//	case "Redis":
+//		buildRedisMap(&mNode, sd)
+//	}
+//	if mapType != "" {
+//		mNode.Nid = traceRoot.Index
+//		traceRoot.RootData.Maps = append(traceRoot.RootData.Maps, mNode)
+//	}
+//	return mNode
+//}
 
 //func buildAndAssemblyMapFromEvent(event tracesdk.Event, traceRoot *RootDataT) MapInfoT {
 //	mNode := buildMapNodeFromEvent(event)
@@ -621,49 +600,49 @@ func buildAndAssemblyMap(sd apmTraceSpan, traceRoot *TraceMapT) MapInfoT {
 //	return mNode
 //}
 
-func buildAppMap(mNode *MapInfoT, traceRoot *TraceMapT, sd apmTraceSpan) {
-	mNode.ServiceName = GO_SERVICE_NAME
-	mNode.ServiceType = APP_SERVICE_TYPE
-	mNode.MethodName = "net/http.(*Transport).roundTrip()"
-	mNode.Level = 1
-	mNode.Pid = 0
-	mNode.Nid = 1
-	// 构建root节点
-	traceRoot.RootData.RespTime = mNode.PureTime
-	traceRoot.RootData.CollTime = mNode.StartTime
-	traceRoot.Index = 1
-	for _, attr := range sd.Attributes() {
-		fmt.Println(attr.Key, ":", attr.Value.AsInterface())
-		switch attr.Key {
-		case "http.uri":
-			traceRoot.RootData.Uri = attr.Value.AsString()
-		case "http.method":
-			traceRoot.RootData.HttpMethod = attr.Value.AsString()
-		case "http.status_code":
-			traceRoot.RootData.HttpCode = attr.Value.AsInt64()
-		case "net.peer.name":
-			traceRoot.RootData.ClientIp = attr.Value.AsString()
-			traceRoot.RootData.Sip = attr.Value.AsString()
-			traceRoot.RootData.Sn = attr.Value.AsString()
-		case "net.peer.port":
-			traceRoot.RootData.Sport = attr.Value.AsInt64()
-			traceRoot.RootData.LocalPort = attr.Value.AsInt64()
-		case "server.trace_id_from":
-			traceRoot.RootData.TraceId = attr.Value.AsString()
-		case "server.called_id":
-			traceRoot.RootData.CalledId = attr.Value.AsInt64()
-		case "server.instance_id_from":
-			traceRoot.RootData.InstanceIdFrom = attr.Value.AsInt64()
-		case "server.app_id_from":
-			traceRoot.RootData.AppIdFrom = attr.Value.AsInt64()
-		case "server.span_id_from":
-			traceRoot.RootData.SpanIdFrom = attr.Value.AsString()
-		case "server.type_from":
-			traceRoot.RootData.TypeFrom = attr.Value.AsString()
-		}
-	}
-
-}
+//func buildAppMap(mNode *MapInfoT, traceRoot *TraceMapT, sd apmTraceSpan) {
+//	mNode.ServiceName = GO_SERVICE_NAME
+//	mNode.ServiceType = APP_SERVICE_TYPE
+//	mNode.MethodName = "net/http.(*Transport).roundTrip()"
+//	mNode.Level = 1
+//	mNode.Pid = 0
+//	mNode.Nid = 1
+//	// 构建root节点
+//	traceRoot.RootData.RespTime = mNode.PureTime
+//	traceRoot.RootData.CollTime = mNode.StartTime
+//	traceRoot.Index = 1
+//	for _, attr := range sd.Attributes() {
+//		fmt.Println(attr.Key, ":", attr.Value.AsInterface())
+//		switch attr.Key {
+//		case "http.uri":
+//			traceRoot.RootData.Uri = attr.Value.AsString()
+//		case "http.method":
+//			traceRoot.RootData.HttpMethod = attr.Value.AsString()
+//		case "http.status_code":
+//			traceRoot.RootData.HttpCode = attr.Value.AsInt64()
+//		case "net.peer.name":
+//			traceRoot.RootData.ClientIp = attr.Value.AsString()
+//			traceRoot.RootData.Sip = attr.Value.AsString()
+//			traceRoot.RootData.Sn = attr.Value.AsString()
+//		case "net.peer.port":
+//			traceRoot.RootData.Sport = attr.Value.AsInt64()
+//			traceRoot.RootData.LocalPort = attr.Value.AsInt64()
+//		case "server.trace_id_from":
+//			traceRoot.RootData.TraceId = attr.Value.AsString()
+//		case "server.called_id":
+//			traceRoot.RootData.CalledId = attr.Value.AsInt64()
+//		case "server.instance_id_from":
+//			traceRoot.RootData.InstanceIdFrom = attr.Value.AsInt64()
+//		case "server.app_id_from":
+//			traceRoot.RootData.AppIdFrom = attr.Value.AsInt64()
+//		case "server.span_id_from":
+//			traceRoot.RootData.SpanIdFrom = attr.Value.AsString()
+//		case "server.type_from":
+//			traceRoot.RootData.TypeFrom = attr.Value.AsString()
+//		}
+//	}
+//
+//}
 
 func buildAppMapFromEvent(traceRoot *RootDataT, sd apmTraceSpan) int {
 	mNode := MapInfoT{
@@ -728,37 +707,48 @@ func buildAppMapFromEvent(traceRoot *RootDataT, sd apmTraceSpan) int {
 			mNode.WallTime = uint64(attr.Value.AsInt64()) / 1e3
 		case "server.code_type":
 			code_type = attr.Value.AsInt64()
+		case "server.app_name":
+			traceRoot.AppName = attr.Value.AsString()
+		case "server.service_name":
+			traceRoot.ServiceName = attr.Value.AsString()
+			mNode.ServiceName = attr.Value.AsString()
+		case "server.app_id":
+			traceRoot.AppId = attr.Value.AsInt64()
+		case "server.agent_id":
+			traceRoot.AgentId = attr.Value.AsInt64()
+		case "server.instance_id":
+			traceRoot.InstanceId = attr.Value.AsInt64()
 		}
 	}
 	traceRoot.Maps = append(traceRoot.Maps, mNode)
 	return int(code_type)
 }
 
-func buildHttpMap(mNode *MapInfoT, sd apmTraceSpan) {
-	mNode.ServiceName = HTTP_SERVICE_NAME
-	mNode.ServiceType = HTTP_SERVICE_TYPE
-	mNode.Schema = "http"
-	mNode.MethodName = "net/http.serverHandler.ServeHTTP()"
-	var descAddr string
-	for _, attr := range sd.Attributes() {
-		//fmt.Println(attr.Key, ":", attr.Value.AsInterface())
-		switch attr.Key {
-		case "http.ip":
-			mNode.Ip = attr.Value.AsString()
-			descAddr += mNode.Ip
-		case "http.port":
-			mNode.Port = attr.Value.AsInt64()
-			descAddr += ":" + attr.Value.AsString()
-		case "http.uri":
-			mNode.Uri = attr.Value.AsString()
-		case "http.assumed_app_id":
-			mNode.AssumedAppId = attr.Value.AsInt64()
-		case "http.span_id":
-			mNode.SpanId = attr.Value.AsString()
-		}
-	}
-	//mNode.AssumedAppId = Md5ToInt64(descAddr, 16)
-}
+//func buildHttpMap(mNode *MapInfoT, sd apmTraceSpan) {
+//	mNode.ServiceName = HTTP_SERVICE_NAME
+//	mNode.ServiceType = HTTP_SERVICE_TYPE
+//	mNode.Schema = "http"
+//	mNode.MethodName = "net/http.serverHandler.ServeHTTP()"
+//	var descAddr string
+//	for _, attr := range sd.Attributes() {
+//		//fmt.Println(attr.Key, ":", attr.Value.AsInterface())
+//		switch attr.Key {
+//		case "http.ip":
+//			mNode.Ip = attr.Value.AsString()
+//			descAddr += mNode.Ip
+//		case "http.port":
+//			mNode.Port = attr.Value.AsInt64()
+//			descAddr += ":" + attr.Value.AsString()
+//		case "http.uri":
+//			mNode.Uri = attr.Value.AsString()
+//		case "http.assumed_app_id":
+//			mNode.AssumedAppId = attr.Value.AsInt64()
+//		case "http.span_id":
+//			mNode.SpanId = attr.Value.AsString()
+//		}
+//	}
+//	//mNode.AssumedAppId = Md5ToInt64(descAddr, 16)
+//}
 
 func buildHttpMapFromEvent(mNode *MapInfoT, event tracesdk.Event) {
 	mNode.ServiceName = HTTP_SERVICE_NAME
@@ -796,28 +786,28 @@ func buildHttpMapFromEvent(mNode *MapInfoT, event tracesdk.Event) {
 	//mNode.AssumedAppId = Md5ToInt64(descAddr, 16)
 }
 
-func buildMysqlMap(mNode *MapInfoT, sd apmTraceSpan) {
-	mNode.Dbn = "unknown"
-	mNode.ServiceName = MYSQL_SERVICE_NAME
-	mNode.ServiceType = SQL_SERVICE_TYPE
-	mNode.MethodName = "database/sql.Query()"
-	for _, attr := range sd.Attributes() {
-		//fmt.Println(attr.Key, ":", attr.Value.AsInterface())
-		switch attr.Key {
-		case "net.peer.name":
-			mNode.Ip = attr.Value.AsString()
-		case "net.peer.port":
-			mNode.Port = attr.Value.AsInt64()
-		case "db.statement":
-			query := attr.Value.AsString()
-			mNode.Ps = []string{query}
-			words := strings.Fields(query)
-			if len(words) > 0 {
-				mNode.OperType = strings.ToUpper(words[0])
-			}
-		}
-	}
-}
+//func buildMysqlMap(mNode *MapInfoT, sd apmTraceSpan) {
+//	mNode.Dbn = "unknown"
+//	mNode.ServiceName = MYSQL_SERVICE_NAME
+//	mNode.ServiceType = SQL_SERVICE_TYPE
+//	mNode.MethodName = "database/sql.Query()"
+//	for _, attr := range sd.Attributes() {
+//		//fmt.Println(attr.Key, ":", attr.Value.AsInterface())
+//		switch attr.Key {
+//		case "net.peer.name":
+//			mNode.Ip = attr.Value.AsString()
+//		case "net.peer.port":
+//			mNode.Port = attr.Value.AsInt64()
+//		case "db.statement":
+//			query := attr.Value.AsString()
+//			mNode.Ps = []string{query}
+//			words := strings.Fields(query)
+//			if len(words) > 0 {
+//				mNode.OperType = strings.ToUpper(words[0])
+//			}
+//		}
+//	}
+//}
 
 func buildMysqlMapEvent(mNode *MapInfoT, event tracesdk.Event) {
 	mNode.Dbn = "unknown"

+ 48 - 14
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/go.mod

@@ -1,28 +1,58 @@
 module go.opentelemetry.io/otel/exporters/otlp/otlptrace
 
-go 1.20
+go 1.21
+
+toolchain go1.21.3
 
 require (
-	github.com/google/go-cmp v0.5.9
+	github.com/coroot/coroot-node-agent v0.0.0
+	github.com/google/go-cmp v0.6.0
 	github.com/stretchr/testify v1.8.4
-	go.opentelemetry.io/otel v1.19.0
-	go.opentelemetry.io/otel/sdk v1.19.0
-	go.opentelemetry.io/otel/trace v1.19.0
+	go.opentelemetry.io/otel v1.22.0
+	go.opentelemetry.io/otel/sdk v1.22.0
+	go.opentelemetry.io/otel/trace v1.22.0
 	go.opentelemetry.io/proto/otlp v1.0.0
-	google.golang.org/protobuf v1.31.0
+	google.golang.org/protobuf v1.32.0
 )
 
 require (
-	github.com/davecgh/go-spew v1.1.1 // indirect
-	github.com/go-logr/logr v1.2.4 // indirect
+	github.com/BurntSushi/toml v1.4.0 // indirect
+	github.com/agoda-com/opentelemetry-logs-go v0.4.1 // indirect
+	github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
+	github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect
+	github.com/cenkalti/backoff/v4 v4.2.1 // indirect
+	github.com/cilium/ebpf v0.11.0 // indirect
+	github.com/coreos/go-systemd/v22 v22.5.0 // indirect
+	github.com/coroot/logparser v1.1.2 // indirect
+	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+	github.com/go-logr/logr v1.4.1 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
-	github.com/kr/pretty v0.3.1 // indirect
-	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/rogpeppe/go-internal v1.10.0 // indirect
-	go.opentelemetry.io/otel/metric v1.19.0 // indirect
-	golang.org/x/sys v0.12.0 // indirect
-	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
+	github.com/golang/protobuf v1.5.3 // indirect
+	github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
+	github.com/hashicorp/go-version v1.6.0 // indirect
+	github.com/natefinch/lumberjack v0.0.0 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+	github.com/sirupsen/logrus v1.9.3 // indirect
+	github.com/vishvananda/netlink v1.2.1-beta.2.0.20220608195807-1a118fe229fc // indirect
+	github.com/vishvananda/netns v0.0.4 // indirect
+	go.mongodb.org/mongo-driver v1.13.1 // indirect
+	go.opentelemetry.io/otel/metric v1.22.0 // indirect
+	go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
+	go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect
+	golang.org/x/arch v0.4.0 // indirect
+	golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
+	golang.org/x/mod v0.16.0 // indirect
+	golang.org/x/net v0.22.0 // indirect
+	golang.org/x/sys v0.18.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
+	google.golang.org/grpc v1.61.0 // indirect
+	gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
+	gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
+	inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect
 )
 
 replace go.opentelemetry.io/otel => ../../..
@@ -32,3 +62,7 @@ replace go.opentelemetry.io/otel/sdk => ../../../sdk
 replace go.opentelemetry.io/otel/trace => ../../../trace
 
 replace go.opentelemetry.io/otel/metric => ../../../metric
+
+replace github.com/natefinch/lumberjack => ../../../../../../pkg/lumberjack
+
+replace github.com/coroot/coroot-node-agent => ../../../../../../

+ 156 - 18
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/go.sum

@@ -1,40 +1,178 @@
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
+github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
+github.com/agoda-com/opentelemetry-logs-go v0.4.1 h1:PWGqIxkEEg4HIjnHsHmNa+yGu0lhxHz4XPGKeT4o6T0=
+github.com/agoda-com/opentelemetry-logs-go v0.4.1/go.mod h1:CeDuVaK9yCWN+8UjOW8AciYJE0rl7K/mw4ejBntGYkc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs=
+github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
+github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
+github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
+github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
+github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
+github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA=
+github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA=
+github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coroot/logparser v1.1.2 h1:9aH4zIBle14xMHq07YHqVFE2t68k3LE10X2yKHXtJG8=
+github.com/coroot/logparser v1.1.2/go.mod h1:YfYxn9FYBm5GYHHUB4zI22irFAWVDe2bcbOWDHKSmEo=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+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/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
+github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
+github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
+github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
-github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
+github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
+github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+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/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
+github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
+github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc=
+github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
-github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
-github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+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/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/vishvananda/netlink v1.2.1-beta.2.0.20220608195807-1a118fe229fc h1:2wzJ1cBcM23GetRJs2y6ETXrFMvp6HefTbFWtqviHZQ=
+github.com/vishvananda/netlink v1.2.1-beta.2.0.20220608195807-1a118fe229fc/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
+github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
+github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
+github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
+github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk=
+go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
 go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
 go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
-golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
-golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=
+go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
+go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
+go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 h1:WJhcL4p+YeDxmZWg141nRm7XC8IDmhz7lk5GpadO1Sg=
+go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
+golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc=
+golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
+golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
+golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
+golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
+golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg=
+google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0=
+google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac h1:OZkkudMUu9LVQMCoRUbI/1p5VCo9BOrlvkqMvWtqa6s=
+google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
+google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
+google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
+google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a h1:1XCVEdxrvL6c0TGOhecLuB7U9zYNdxZEjvOqJreKZiM=
+inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a/go.mod h1:e83i32mAQOW1LAqEIweALsuK2Uw4mhQadA5r7b0Wobo=

+ 5 - 5
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/go.mod

@@ -13,8 +13,8 @@ require (
 	go.opentelemetry.io/otel/trace v1.22.0
 	go.opentelemetry.io/proto/otlp v1.0.0
 	go.uber.org/goleak v1.2.1
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98
-	google.golang.org/grpc v1.58.2
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac
+	google.golang.org/grpc v1.61.0
 	google.golang.org/protobuf v1.32.0
 )
 
@@ -27,10 +27,10 @@ require (
 	github.com/kr/text v0.2.0 // indirect
 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
 	go.opentelemetry.io/otel/metric v1.22.0 // indirect
-	golang.org/x/net v0.12.0 // indirect
+	golang.org/x/net v0.22.0 // indirect
 	golang.org/x/sys v0.18.0 // indirect
-	golang.org/x/text v0.11.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
+	golang.org/x/text v0.14.0 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
 

+ 5 - 0
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc/go.sum

@@ -33,19 +33,24 @@ go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
 go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
 golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
 golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
+golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
 golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
 golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
 golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g=
 google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
 google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
+google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
 google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
 google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
+google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=

+ 3 - 3
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/client_apm.go

@@ -5,7 +5,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"github.com/coroot/coroot-node-agent/common"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
 	"go.opentelemetry.io/otel"
 	"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
 	"io"
@@ -26,7 +26,7 @@ func (d *client) UploadApmTraces(ctx context.Context, rootData []otlptrace.RootD
 	ctx, cancel := d.contextWithStop(ctx)
 	defer cancel()
 	mapLen := len(rootData)
-	request, err := d.newApmRequest(rawRequest, mapLen, common.CodeType(codeType))
+	request, err := d.newApmRequest(rawRequest, mapLen, CodeType(codeType))
 	if err != nil {
 		return err
 	}
@@ -91,7 +91,7 @@ type RespDataT struct {
 	Msg  string `json:"message"`
 }
 
-func (d *client) newApmRequest(body []byte, mapLen int, codeType common.CodeType) (request, error) {
+func (d *client) newApmRequest(body []byte, mapLen int, codeType CodeType) (request, error) {
 	u := url.URL{Scheme: d.getScheme(), Host: d.cfg.Endpoint, Path: d.cfg.URLPath}
 	r, err := http.NewRequest(http.MethodPost, u.String(), nil)
 	if err != nil {

+ 23 - 2
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/go.mod

@@ -6,7 +6,7 @@ toolchain go1.21.3
 
 require (
 	github.com/cenkalti/backoff/v4 v4.2.1
-	github.com/coroot/coroot-node-agent v0.0.0-00010101000000-000000000000
+	github.com/coroot/coroot-node-agent v0.0.0
 	github.com/stretchr/testify v1.8.4
 	go.opentelemetry.io/otel v1.22.0
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0
@@ -18,20 +18,39 @@ require (
 )
 
 require (
+	github.com/agoda-com/opentelemetry-logs-go v0.4.1 // indirect
+	github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
+	github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect
+	github.com/cilium/ebpf v0.11.0 // indirect
+	github.com/coreos/go-systemd/v22 v22.5.0 // indirect
+	github.com/coroot/logparser v1.1.2 // indirect
 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 	github.com/go-logr/logr v1.4.1 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
 	github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
-	github.com/kr/text v0.2.0 // indirect
+	github.com/hashicorp/go-version v1.6.0 // indirect
+	github.com/natefinch/lumberjack v0.0.0 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+	github.com/sirupsen/logrus v1.9.3 // indirect
+	github.com/vishvananda/netlink v1.2.1-beta.2.0.20220608195807-1a118fe229fc // indirect
+	github.com/vishvananda/netns v0.0.4 // indirect
+	go.mongodb.org/mongo-driver v1.13.1 // indirect
 	go.opentelemetry.io/otel/metric v1.22.0 // indirect
+	go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
+	go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect
+	golang.org/x/arch v0.4.0 // indirect
+	golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
+	golang.org/x/mod v0.16.0 // indirect
 	golang.org/x/net v0.22.0 // indirect
 	golang.org/x/sys v0.18.0 // indirect
 	golang.org/x/text v0.14.0 // indirect
 	google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac // indirect
+	gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
+	inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect
 )
 
 replace go.opentelemetry.io/otel/exporters/otlp/otlptrace => ../
@@ -45,3 +64,5 @@ replace go.opentelemetry.io/otel/trace => ../../../../trace
 replace go.opentelemetry.io/otel/metric => ../../../../metric
 
 replace github.com/coroot/coroot-node-agent => ../../../../../../../
+
+replace github.com/natefinch/lumberjack => ../../../../../../lumberjack

+ 141 - 23
pkg/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp/go.sum

@@ -1,60 +1,178 @@
+github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
+github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
+github.com/agoda-com/opentelemetry-logs-go v0.4.1 h1:PWGqIxkEEg4HIjnHsHmNa+yGu0lhxHz4XPGKeT4o6T0=
+github.com/agoda-com/opentelemetry-logs-go v0.4.1/go.mod h1:CeDuVaK9yCWN+8UjOW8AciYJE0rl7K/mw4ejBntGYkc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 h1:ez/4by2iGztzR4L0zgAOR8lTQK9VlyBVVd7G4omaOQs=
+github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
 github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
 github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
+github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
+github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA=
+github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA=
+github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coroot/logparser v1.1.2 h1:9aH4zIBle14xMHq07YHqVFE2t68k3LE10X2yKHXtJG8=
+github.com/coroot/logparser v1.1.2/go.mod h1:YfYxn9FYBm5GYHHUB4zI22irFAWVDe2bcbOWDHKSmEo=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+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/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
+github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
+github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
+github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
-github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
 github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/godbus/dbus/v5 v5.0.6 h1:mkgN1ofwASrYnJ5W6U/BxG15eXXXjirgZc7CLqkcaro=
+github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
+github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
 github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+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/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
 github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
+github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
+github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417 h1:3snG66yBm59tKhhSPQrQ/0bCrv1LQbKt40LnUPiUxdc=
+github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+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/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/vishvananda/netlink v1.2.1-beta.2.0.20220608195807-1a118fe229fc h1:2wzJ1cBcM23GetRJs2y6ETXrFMvp6HefTbFWtqviHZQ=
+github.com/vishvananda/netlink v1.2.1-beta.2.0.20220608195807-1a118fe229fc/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
+github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
+github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
+github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
+github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk=
+go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo=
 go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
 go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
-golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
-golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=
+go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
+go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
+go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 h1:WJhcL4p+YeDxmZWg141nRm7XC8IDmhz7lk5GpadO1Sg=
+go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
+golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc=
+golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
+golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
+golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
 golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
-golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
-golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
-golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g=
-google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
-google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917 h1:nz5NESFLZbJGPFxDT/HCn+V1mZ8JGNoY4nUpmW/Y2eg=
+google.golang.org/genproto v0.0.0-20240102182953-50ed04b92917/go.mod h1:pZqR+glSb11aJ+JQcczCvgf47+duRuzNSKqE8YAQnV0=
+google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac h1:OZkkudMUu9LVQMCoRUbI/1p5VCo9BOrlvkqMvWtqa6s=
 google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac h1:nUQEQmH/csSvFECKYRv6HWEyypysidKl2I6Qpsglq/0=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:daQN87bsDqDoe316QbbvX60nMoJQa4r6Ds0ZuoAe5yA=
-google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
-google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
+google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
 google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
 google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a h1:1XCVEdxrvL6c0TGOhecLuB7U9zYNdxZEjvOqJreKZiM=
+inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a/go.mod h1:e83i32mAQOW1LAqEIweALsuK2Uw4mhQadA5r7b0Wobo=

+ 23 - 0
pkg/lumberjack/.gitignore

@@ -0,0 +1,23 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test

+ 6 - 0
pkg/lumberjack/.travis.yml

@@ -0,0 +1,6 @@
+language: go
+
+go:
+  - 1.8
+  - 1.7
+  - 1.6

+ 21 - 0
pkg/lumberjack/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Nate Finch 
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 179 - 0
pkg/lumberjack/README.md

@@ -0,0 +1,179 @@
+# lumberjack  [![GoDoc](https://godoc.org/gopkg.in/natefinch/lumberjack.v2?status.png)](https://godoc.org/gopkg.in/natefinch/lumberjack.v2) [![Build Status](https://travis-ci.org/natefinch/lumberjack.svg?branch=v2.0)](https://travis-ci.org/natefinch/lumberjack) [![Build status](https://ci.appveyor.com/api/projects/status/00gchpxtg4gkrt5d)](https://ci.appveyor.com/project/natefinch/lumberjack) [![Coverage Status](https://coveralls.io/repos/natefinch/lumberjack/badge.svg?branch=v2.0)](https://coveralls.io/r/natefinch/lumberjack?branch=v2.0)
+
+### Lumberjack is a Go package for writing logs to rolling files.
+
+Package lumberjack provides a rolling logger.
+
+Note that this is v2.0 of lumberjack, and should be imported using gopkg.in
+thusly:
+
+    import "gopkg.in/natefinch/lumberjack.v2"
+
+The package name remains simply lumberjack, and the code resides at
+https://github.com/natefinch/lumberjack under the v2.0 branch.
+
+Lumberjack is intended to be one part of a logging infrastructure.
+It is not an all-in-one solution, but instead is a pluggable
+component at the bottom of the logging stack that simply controls the files
+to which logs are written.
+
+Lumberjack plays well with any logging package that can write to an
+io.Writer, including the standard library's log package.
+
+Lumberjack assumes that only one process is writing to the output files.
+Using the same lumberjack configuration from multiple processes on the same
+machine will result in improper behavior.
+
+
+**Example**
+
+To use lumberjack with the standard library's log package, just pass it into the SetOutput function when your application starts.
+
+Code:
+
+```go
+log.SetOutput(&lumberjack.Logger{
+    Filename:   "/var/log/myapp/foo.log",
+    MaxSize:    500, // megabytes
+    MaxBackups: 3,
+    MaxAge:     28, //days
+    Compress:   true, // disabled by default
+})
+```
+
+
+
+## type Logger
+``` go
+type Logger struct {
+    // Filename is the file to write logs to.  Backup log files will be retained
+    // in the same directory.  It uses <processname>-lumberjack.log in
+    // os.TempDir() if empty.
+    Filename string `json:"filename" yaml:"filename"`
+
+    // MaxSize is the maximum size in megabytes of the log file before it gets
+    // rotated. It defaults to 100 megabytes.
+    MaxSize int `json:"maxsize" yaml:"maxsize"`
+
+    // MaxAge is the maximum number of days to retain old log files based on the
+    // timestamp encoded in their filename.  Note that a day is defined as 24
+    // hours and may not exactly correspond to calendar days due to daylight
+    // savings, leap seconds, etc. The default is not to remove old log files
+    // based on age.
+    MaxAge int `json:"maxage" yaml:"maxage"`
+
+    // MaxBackups is the maximum number of old log files to retain.  The default
+    // is to retain all old log files (though MaxAge may still cause them to get
+    // deleted.)
+    MaxBackups int `json:"maxbackups" yaml:"maxbackups"`
+
+    // LocalTime determines if the time used for formatting the timestamps in
+    // backup files is the computer's local time.  The default is to use UTC
+    // time.
+    LocalTime bool `json:"localtime" yaml:"localtime"`
+
+    // Compress determines if the rotated log files should be compressed
+    // using gzip. The default is not to perform compression.
+    Compress bool `json:"compress" yaml:"compress"`
+    // contains filtered or unexported fields
+}
+```
+Logger is an io.WriteCloser that writes to the specified filename.
+
+Logger opens or creates the logfile on first Write.  If the file exists and
+is less than MaxSize megabytes, lumberjack will open and append to that file.
+If the file exists and its size is >= MaxSize megabytes, the file is renamed
+by putting the current time in a timestamp in the name immediately before the
+file's extension (or the end of the filename if there's no extension). A new
+log file is then created using original filename.
+
+Whenever a write would cause the current log file exceed MaxSize megabytes,
+the current file is closed, renamed, and a new log file created with the
+original name. Thus, the filename you give Logger is always the "current" log
+file.
+
+Backups use the log file name given to Logger, in the form `name-timestamp.ext`
+where name is the filename without the extension, timestamp is the time at which
+the log was rotated formatted with the time.Time format of
+`2006-01-02T15-04-05.000` and the extension is the original extension.  For
+example, if your Logger.Filename is `/var/log/foo/server.log`, a backup created
+at 6:30pm on Nov 11 2016 would use the filename
+`/var/log/foo/server-2016-11-04T18-30-00.000.log`
+
+### Cleaning Up Old Log Files
+Whenever a new logfile gets created, old log files may be deleted.  The most
+recent files according to the encoded timestamp will be retained, up to a
+number equal to MaxBackups (or all of them if MaxBackups is 0).  Any files
+with an encoded timestamp older than MaxAge days are deleted, regardless of
+MaxBackups.  Note that the time encoded in the timestamp is the rotation
+time, which may differ from the last time that file was written to.
+
+If MaxBackups and MaxAge are both 0, no old log files will be deleted.
+
+
+
+
+
+
+
+
+
+
+
+### func (\*Logger) Close
+``` go
+func (l *Logger) Close() error
+```
+Close implements io.Closer, and closes the current logfile.
+
+
+
+### func (\*Logger) Rotate
+``` go
+func (l *Logger) Rotate() error
+```
+Rotate causes Logger to close the existing log file and immediately create a
+new one.  This is a helper function for applications that want to initiate
+rotations outside of the normal rotation rules, such as in response to
+SIGHUP.  After rotating, this initiates a cleanup of old log files according
+to the normal rules.
+
+**Example**
+
+Example of how to rotate in response to SIGHUP.
+
+Code:
+
+```go
+l := &lumberjack.Logger{}
+log.SetOutput(l)
+c := make(chan os.Signal, 1)
+signal.Notify(c, syscall.SIGHUP)
+
+go func() {
+    for {
+        <-c
+        l.Rotate()
+    }
+}()
+```
+
+### func (\*Logger) Write
+``` go
+func (l *Logger) Write(p []byte) (n int, err error)
+```
+Write implements io.Writer.  If a write would cause the log file to be larger
+than MaxSize, the file is closed, renamed to include a timestamp of the
+current time, and a new log file is created using the original log file name.
+If the length of the write is greater than MaxSize, an error is returned.
+
+
+
+
+
+
+
+
+
+- - -
+Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md)

+ 11 - 0
pkg/lumberjack/chown.go

@@ -0,0 +1,11 @@
+// +build !linux
+
+package lumberjack
+
+import (
+	"os"
+)
+
+func chown(_ string, _ os.FileInfo) error {
+	return nil
+}

+ 19 - 0
pkg/lumberjack/chown_linux.go

@@ -0,0 +1,19 @@
+package lumberjack
+
+import (
+	"os"
+	"syscall"
+)
+
+// os_Chown is a var so we can mock it out during tests.
+var os_Chown = os.Chown
+
+func chown(name string, info os.FileInfo) error {
+	f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, info.Mode())
+	if err != nil {
+		return err
+	}
+	f.Close()
+	stat := info.Sys().(*syscall.Stat_t)
+	return os_Chown(name, int(stat.Uid), int(stat.Gid))
+}

+ 19 - 0
pkg/lumberjack/example_test.go

@@ -0,0 +1,19 @@
+package lumberjack_test
+
+import (
+	"log"
+
+	"gopkg.in/natefinch/lumberjack.v2"
+)
+
+// To use lumberjack with the standard library's log package, just pass it into
+// the SetOutput function when your application starts.
+func Example() {
+	log.SetOutput(&lumberjack.Logger{
+		Filename:   "/var/log/myapp/foo.log",
+		MaxSize:    500, // megabytes
+		MaxBackups: 3,
+		MaxAge:     28, // days
+		Compress:   true, // disabled by default
+	})
+}

+ 7 - 0
pkg/lumberjack/go.mod

@@ -0,0 +1,7 @@
+module github.com/natefinch/lumberjack
+
+go 1.12
+
+require (
+
+)

+ 205 - 0
pkg/lumberjack/linux_test.go

@@ -0,0 +1,205 @@
+// +build linux
+
+package lumberjack
+
+import (
+	"os"
+	"syscall"
+	"testing"
+	"time"
+)
+
+func TestMaintainMode(t *testing.T) {
+	currentTime = fakeTime
+	dir := makeTempDir("TestMaintainMode", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+
+	mode := os.FileMode(0600)
+	f, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, mode)
+	isNil(err, t)
+	f.Close()
+
+	l := &Logger{
+		Filename:   filename,
+		MaxBackups: 1,
+		MaxSize:    100, // megabytes
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	newFakeTime()
+
+	err = l.Rotate()
+	isNil(err, t)
+
+	filename2 := backupFile(dir)
+	info, err := os.Stat(filename)
+	isNil(err, t)
+	info2, err := os.Stat(filename2)
+	isNil(err, t)
+	equals(mode, info.Mode(), t)
+	equals(mode, info2.Mode(), t)
+}
+
+func TestMaintainOwner(t *testing.T) {
+	fakeFS := newFakeFS()
+	os_Chown = fakeFS.Chown
+	os_Stat = fakeFS.Stat
+	defer func() {
+		os_Chown = os.Chown
+		os_Stat = os.Stat
+	}()
+	currentTime = fakeTime
+	dir := makeTempDir("TestMaintainOwner", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+
+	f, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, 0644)
+	isNil(err, t)
+	f.Close()
+
+	l := &Logger{
+		Filename:   filename,
+		MaxBackups: 1,
+		MaxSize:    100, // megabytes
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	newFakeTime()
+
+	err = l.Rotate()
+	isNil(err, t)
+
+	equals(555, fakeFS.files[filename].uid, t)
+	equals(666, fakeFS.files[filename].gid, t)
+}
+
+func TestCompressMaintainMode(t *testing.T) {
+	currentTime = fakeTime
+
+	dir := makeTempDir("TestCompressMaintainMode", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+
+	mode := os.FileMode(0600)
+	f, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, mode)
+	isNil(err, t)
+	f.Close()
+
+	l := &Logger{
+		Compress: true,
+		Filename:   filename,
+		MaxBackups: 1,
+		MaxSize:    100, // megabytes
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	newFakeTime()
+
+	err = l.Rotate()
+	isNil(err, t)
+
+	// we need to wait a little bit since the files get compressed on a different
+	// goroutine.
+	<-time.After(10 * time.Millisecond)
+
+	// a compressed version of the log file should now exist with the correct
+	// mode.
+	filename2 := backupFile(dir)
+	info, err := os.Stat(filename)
+	isNil(err, t)
+	info2, err := os.Stat(filename2+compressSuffix)
+	isNil(err, t)
+	equals(mode, info.Mode(), t)
+	equals(mode, info2.Mode(), t)
+}
+
+func TestCompressMaintainOwner(t *testing.T) {
+	fakeFS := newFakeFS()
+	os_Chown = fakeFS.Chown
+	os_Stat = fakeFS.Stat
+	defer func() {
+		os_Chown = os.Chown
+		os_Stat = os.Stat
+	}()
+	currentTime = fakeTime
+	dir := makeTempDir("TestCompressMaintainOwner", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+
+	f, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, 0644)
+	isNil(err, t)
+	f.Close()
+
+	l := &Logger{
+		Compress:   true,
+		Filename:   filename,
+		MaxBackups: 1,
+		MaxSize:    100, // megabytes
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	newFakeTime()
+
+	err = l.Rotate()
+	isNil(err, t)
+
+	// we need to wait a little bit since the files get compressed on a different
+	// goroutine.
+	<-time.After(10 * time.Millisecond)
+
+	// a compressed version of the log file should now exist with the correct
+	// owner.
+	filename2 := backupFile(dir)
+	equals(555, fakeFS.files[filename2+compressSuffix].uid, t)
+	equals(666, fakeFS.files[filename2+compressSuffix].gid, t)
+}
+
+type fakeFile struct {
+	uid int
+	gid int
+}
+
+type fakeFS struct {
+	files map[string]fakeFile
+}
+
+func newFakeFS() *fakeFS {
+	return &fakeFS{files: make(map[string]fakeFile)}
+}
+
+func (fs *fakeFS) Chown(name string, uid, gid int) error {
+	fs.files[name] = fakeFile{uid: uid, gid: gid}
+	return nil
+}
+
+func (fs *fakeFS) Stat(name string) (os.FileInfo, error) {
+	info, err := os.Stat(name)
+	if err != nil {
+		return nil, err
+	}
+	stat := info.Sys().(*syscall.Stat_t)
+	stat.Uid = 555
+	stat.Gid = 666
+	return info, nil
+}

+ 562 - 0
pkg/lumberjack/lumberjack.go

@@ -0,0 +1,562 @@
+// Package lumberjack provides a rolling logger.
+//
+// Note that this is v2.0 of lumberjack, and should be imported using gopkg.in
+// thusly:
+//
+//   import "gopkg.in/natefinch/lumberjack.v2"
+//
+// The package name remains simply lumberjack, and the code resides at
+// https://github.com/natefinch/lumberjack under the v2.0 branch.
+//
+// Lumberjack is intended to be one part of a logging infrastructure.
+// It is not an all-in-one solution, but instead is a pluggable
+// component at the bottom of the logging stack that simply controls the files
+// to which logs are written.
+//
+// Lumberjack plays well with any logging package that can write to an
+// io.Writer, including the standard library's log package.
+//
+// Lumberjack assumes that only one process is writing to the output files.
+// Using the same lumberjack configuration from multiple processes on the same
+// machine will result in improper behavior.
+package lumberjack
+
+import (
+	"compress/gzip"
+	"errors"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+	"sync"
+	"time"
+)
+
+const (
+	backupTimeFormat = "2006-01-02T15-04-05.000"
+	compressSuffix   = ".gz"
+	defaultMaxSize   = 100
+)
+
+// ensure we always implement io.WriteCloser
+var _ io.WriteCloser = (*Logger)(nil)
+
+// Logger is an io.WriteCloser that writes to the specified filename.
+//
+// Logger opens or creates the logfile on first Write.  If the file exists and
+// is less than MaxSize megabytes, lumberjack will open and append to that file.
+// If the file exists and its size is >= MaxSize megabytes, the file is renamed
+// by putting the current time in a timestamp in the name immediately before the
+// file's extension (or the end of the filename if there's no extension). A new
+// log file is then created using original filename.
+//
+// Whenever a write would cause the current log file exceed MaxSize megabytes,
+// the current file is closed, renamed, and a new log file created with the
+// original name. Thus, the filename you give Logger is always the "current" log
+// file.
+//
+// Backups use the log file name given to Logger, in the form
+// `name-timestamp.ext` where name is the filename without the extension,
+// timestamp is the time at which the log was rotated formatted with the
+// time.Time format of `2006-01-02T15-04-05.000` and the extension is the
+// original extension.  For example, if your Logger.Filename is
+// `/var/log/foo/server.log`, a backup created at 6:30pm on Nov 11 2016 would
+// use the filename `/var/log/foo/server-2016-11-04T18-30-00.000.log`
+//
+// Cleaning Up Old Log Files
+//
+// Whenever a new logfile gets created, old log files may be deleted.  The most
+// recent files according to the encoded timestamp will be retained, up to a
+// number equal to MaxBackups (or all of them if MaxBackups is 0).  Any files
+// with an encoded timestamp older than MaxAge days are deleted, regardless of
+// MaxBackups.  Note that the time encoded in the timestamp is the rotation
+// time, which may differ from the last time that file was written to.
+//
+// If MaxBackups and MaxAge are both 0, no old log files will be deleted.
+type Logger struct {
+	// Filename is the file to write logs to.  Backup log files will be retained
+	// in the same directory.  It uses <processname>-lumberjack.log in
+	// os.TempDir() if empty.
+	Filename string `json:"filename" yaml:"filename"`
+
+	// MaxSize is the maximum size in megabytes of the log file before it gets
+	// rotated. It defaults to 100 megabytes.
+	MaxSize int `json:"maxsize" yaml:"maxsize"`
+
+	// MaxAge is the maximum number of days to retain old log files based on the
+	// timestamp encoded in their filename.  Note that a day is defined as 24
+	// hours and may not exactly correspond to calendar days due to daylight
+	// savings, leap seconds, etc. The default is not to remove old log files
+	// based on age.
+	MaxAge int `json:"maxage" yaml:"maxage"`
+
+	// MaxBackups is the maximum number of old log files to retain.  The default
+	// is to retain all old log files (though MaxAge may still cause them to get
+	// deleted.)
+	MaxBackups int `json:"maxbackups" yaml:"maxbackups"`
+
+	// LocalTime determines if the time used for formatting the timestamps in
+	// backup files is the computer's local time.  The default is to use UTC
+	// time.
+	LocalTime bool `json:"localtime" yaml:"localtime"`
+
+	// Compress determines if the rotated log files should be compressed
+	// using gzip. The default is not to perform compression.
+	Compress bool `json:"compress" yaml:"compress"`
+
+	size int64
+	file *os.File
+	mu   sync.Mutex
+
+	millCh    chan bool
+	startMill sync.Once
+}
+
+var (
+	// currentTime exists so it can be mocked out by tests.
+	currentTime = time.Now
+
+	// os_Stat exists so it can be mocked out by tests.
+	os_Stat = os.Stat
+
+	// megabyte is the conversion factor between MaxSize and bytes.  It is a
+	// variable so tests can mock it out and not need to write megabytes of data
+	// to disk.
+	megabyte = 1024 * 1024
+)
+
+// Write implements io.Writer.  If a write would cause the log file to be larger
+// than MaxSize, the file is closed, renamed to include a timestamp of the
+// current time, and a new log file is created using the original log file name.
+// If the length of the write is greater than MaxSize, an error is returned.
+func (l *Logger) Write(p []byte) (n int, err error) {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+
+	writeLen := int64(len(p))
+	if writeLen > l.max() {
+		return 0, fmt.Errorf(
+			"write length %d exceeds maximum file size %d", writeLen, l.max(),
+		)
+	}
+
+	if l.file == nil {
+		if err = l.openExistingOrNew(len(p)); err != nil {
+			return 0, err
+		}
+	}
+
+	if l.size+writeLen > l.max() {
+		if err := l.rotate(); err != nil {
+			return 0, err
+		}
+	}
+
+	n, err = l.file.Write(p)
+	l.size += int64(n)
+
+	return n, err
+}
+
+// Close implements io.Closer, and closes the current logfile.
+func (l *Logger) Close() error {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	return l.close()
+}
+
+// close closes the file if it is open.
+func (l *Logger) close() error {
+	if l.file == nil {
+		return nil
+	}
+	err := l.file.Close()
+	l.file = nil
+	return err
+}
+
+// Rotate causes Logger to close the existing log file and immediately create a
+// new one.  This is a helper function for applications that want to initiate
+// rotations outside of the normal rotation rules, such as in response to
+// SIGHUP.  After rotating, this initiates compression and removal of old log
+// files according to the configuration.
+func (l *Logger) Rotate() error {
+	l.mu.Lock()
+	defer l.mu.Unlock()
+	return l.rotate()
+}
+
+// rotate closes the current file, moves it aside with a timestamp in the name,
+// (if it exists), opens a new file with the original filename, and then runs
+// post-rotation processing and removal.
+func (l *Logger) rotate() error {
+	if err := l.close(); err != nil {
+		return err
+	}
+	if err := l.openNew(); err != nil {
+		return err
+	}
+	l.mill()
+	return nil
+}
+
+// openNew opens a new log file for writing, moving any old log file out of the
+// way.  This methods assumes the file has already been closed.
+func (l *Logger) openNew() error {
+	err := os.MkdirAll(l.dir(), 0744)
+	if err != nil {
+		return fmt.Errorf("can't make directories for new logfile: %s", err)
+	}
+
+	name := l.filename()
+	mode := os.FileMode(0644)
+	info, err := os_Stat(name)
+	if err == nil {
+		// Copy the mode off the old logfile.
+		mode = info.Mode()
+		// move the existing file
+		newname := backupName(name, l.LocalTime)
+		if err := os.Rename(name, newname); err != nil {
+			return fmt.Errorf("can't rename log file: %s", err)
+		}
+
+		// this is a no-op anywhere but linux
+		if err := chown(name, info); err != nil {
+			return err
+		}
+	}
+
+	// we use truncate here because this should only get called when we've moved
+	// the file ourselves. if someone else creates the file in the meantime,
+	// just wipe out the contents.
+	f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode)
+	if err != nil {
+		return fmt.Errorf("can't open new logfile: %s", err)
+	}
+	l.file = f
+	l.size = 0
+	return nil
+}
+
+// backupName creates a new filename from the given name, inserting a timestamp
+// between the filename and the extension, using the local time if requested
+// (otherwise UTC).
+func backupName(name string, local bool) string {
+	dir := filepath.Dir(name)
+	filename := filepath.Base(name)
+	ext := filepath.Ext(filename)
+	prefix := filename[:len(filename)-len(ext)]
+	t := currentTime()
+	if !local {
+		t = t.UTC()
+	}
+
+	timestamp := t.Format(backupTimeFormat)
+	return filepath.Join(dir, fmt.Sprintf("%s-%s%s", prefix, timestamp, ext))
+}
+
+// openExistingOrNew opens the logfile if it exists and if the current write
+// would not put it over MaxSize.  If there is no such file or the write would
+// put it over the MaxSize, a new file is created.
+func (l *Logger) openExistingOrNew(writeLen int) error {
+	l.mill()
+
+	filename := l.filename()
+	info, err := os_Stat(filename)
+	if os.IsNotExist(err) {
+		return l.openNew()
+	}
+	if err != nil {
+		return fmt.Errorf("error getting log file info: %s", err)
+	}
+
+	if info.Size()+int64(writeLen) >= l.max() {
+		return l.rotate()
+	}
+
+	file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0644)
+	if err != nil {
+		// if we fail to open the old log file for some reason, just ignore
+		// it and open a new log file.
+		return l.openNew()
+	}
+	l.file = file
+	l.size = info.Size()
+	return nil
+}
+
+// genFilename generates the name of the logfile from the current time.
+func (l *Logger) filename() string {
+	if l.Filename != "" {
+		return l.Filename
+	}
+	name := filepath.Base(os.Args[0]) + "-lumberjack.log"
+	return filepath.Join(os.TempDir(), name)
+}
+
+func isToday(date time.Time) bool {
+	now := time.Now()
+	return now.Year() == date.Year() && now.Month() == date.Month() && now.Day() == date.Day()
+}
+
+// millRunOnce performs compression and removal of stale log files.
+// Log files are compressed if enabled via configuration and old log
+// files are removed, keeping at most l.MaxBackups files, as long as
+// none of them are older than MaxAge.
+func (l *Logger) millRunOnce() error {
+	if l.MaxBackups == 0 && l.MaxAge == 0 && !l.Compress {
+		return nil
+	}
+
+	files, err := l.oldLogFiles()
+	if err != nil {
+		return err
+	}
+
+	var compress, remove, todyFiles []logInfo
+
+	for _, f := range files {
+		if isToday(f.timestamp) {
+			todyFiles = append(todyFiles, f)
+		}
+	}
+
+	if l.MaxBackups > 0 && l.MaxBackups < len(todyFiles) {
+		preserved := make(map[string]bool)
+		var remaining []logInfo
+		for _, f := range files {
+			// Only count the uncompressed log file or the
+			// compressed log file, not both.
+			fn := f.Name()
+			if strings.HasSuffix(fn, compressSuffix) {
+				fn = fn[:len(fn)-len(compressSuffix)]
+			}
+			preserved[fn] = true
+
+			if len(preserved) > l.MaxBackups && isToday(f.timestamp) {
+				remove = append(remove, f)
+			} else {
+				remaining = append(remaining, f)
+			}
+		}
+		files = remaining
+	}
+	if l.MaxAge > 0 {
+		diff := time.Duration(int64(24*time.Hour) * int64(l.MaxAge))
+		cutoff := currentTime().Add(-1 * diff)
+
+		var remaining []logInfo
+		for _, f := range files {
+			if l.LocalTime {
+				f.timestamp = f.timestamp.Local()
+			} else {
+				f.timestamp = f.timestamp.UTC()
+			}
+			if f.timestamp.Before(cutoff) {
+				remove = append(remove, f)
+			} else {
+				remaining = append(remaining, f)
+			}
+		}
+		files = remaining
+	}
+
+	if l.Compress {
+		for _, f := range files {
+			if !strings.HasSuffix(f.Name(), compressSuffix) {
+				compress = append(compress, f)
+			}
+		}
+	}
+
+	for _, f := range remove {
+		errRemove := os.Remove(filepath.Join(l.dir(), f.Name()))
+		if err == nil && errRemove != nil {
+			err = errRemove
+		}
+	}
+	for _, f := range compress {
+		fn := filepath.Join(l.dir(), f.Name())
+		errCompress := compressLogFile(fn, fn+compressSuffix)
+		if err == nil && errCompress != nil {
+			err = errCompress
+		}
+	}
+
+	return err
+}
+
+// millRun runs in a goroutine to manage post-rotation compression and removal
+// of old log files.
+func (l *Logger) millRun() {
+	for _ = range l.millCh {
+		// what am I going to do, log this?
+		_ = l.millRunOnce()
+	}
+}
+
+// mill performs post-rotation compression and removal of stale log files,
+// starting the mill goroutine if necessary.
+func (l *Logger) mill() {
+	l.startMill.Do(func() {
+		l.millCh = make(chan bool, 1)
+		go l.millRun()
+	})
+	select {
+	case l.millCh <- true:
+	default:
+	}
+}
+
+// oldLogFiles returns the list of backup log files stored in the same
+// directory as the current log file, sorted by ModTime
+func (l *Logger) oldLogFiles() ([]logInfo, error) {
+	files, err := ioutil.ReadDir(l.dir())
+	if err != nil {
+		return nil, fmt.Errorf("can't read log file directory: %s", err)
+	}
+	logFiles := []logInfo{}
+
+	prefix, ext := l.prefixAndExt()
+
+	for _, f := range files {
+		if f.IsDir() {
+			continue
+		}
+		if t, err := l.timeFromName(f.Name(), prefix, ext); err == nil {
+			logFiles = append(logFiles, logInfo{t, f})
+			continue
+		}
+		if t, err := l.timeFromName(f.Name(), prefix, ext+compressSuffix); err == nil {
+			logFiles = append(logFiles, logInfo{t, f})
+			continue
+		}
+		// error parsing means that the suffix at the end was not generated
+		// by lumberjack, and therefore it's not a backup file.
+	}
+
+	sort.Sort(byFormatTime(logFiles))
+
+	return logFiles, nil
+}
+
+// timeFromName extracts the formatted time from the filename by stripping off
+// the filename's prefix and extension. This prevents someone's filename from
+// confusing time.parse.
+func (l *Logger) timeFromName(filename, prefix, ext string) (time.Time, error) {
+	if !strings.HasPrefix(filename, prefix) {
+		return time.Time{}, errors.New("mismatched prefix")
+	}
+	if !strings.HasSuffix(filename, ext) {
+		return time.Time{}, errors.New("mismatched extension")
+	}
+	ts := filename[len(prefix) : len(filename)-len(ext)]
+	if l.LocalTime {
+		loc, _ := time.LoadLocation("Local")
+		return time.ParseInLocation(backupTimeFormat, ts, loc)
+	} else {
+		return time.Parse(backupTimeFormat, ts)
+	}
+}
+
+// max returns the maximum size in bytes of log files before rolling.
+func (l *Logger) max() int64 {
+	if l.MaxSize == 0 {
+		return int64(defaultMaxSize * megabyte)
+	}
+	return int64(l.MaxSize) * int64(megabyte)
+}
+
+// dir returns the directory for the current filename.
+func (l *Logger) dir() string {
+	return filepath.Dir(l.filename())
+}
+
+// prefixAndExt returns the filename part and extension part from the Logger's
+// filename.
+func (l *Logger) prefixAndExt() (prefix, ext string) {
+	filename := filepath.Base(l.filename())
+	ext = filepath.Ext(filename)
+	prefix = filename[:len(filename)-len(ext)] + "-"
+	return prefix, ext
+}
+
+// compressLogFile compresses the given log file, removing the
+// uncompressed log file if successful.
+func compressLogFile(src, dst string) (err error) {
+	f, err := os.Open(src)
+	if err != nil {
+		return fmt.Errorf("failed to open log file: %v", err)
+	}
+	defer f.Close()
+
+	fi, err := os_Stat(src)
+	if err != nil {
+		return fmt.Errorf("failed to stat log file: %v", err)
+	}
+
+	if err := chown(dst, fi); err != nil {
+		return fmt.Errorf("failed to chown compressed log file: %v", err)
+	}
+
+	// If this file already exists, we presume it was created by
+	// a previous attempt to compress the log file.
+	gzf, err := os.OpenFile(dst, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode())
+	if err != nil {
+		return fmt.Errorf("failed to open compressed log file: %v", err)
+	}
+	defer gzf.Close()
+
+	gz := gzip.NewWriter(gzf)
+
+	defer func() {
+		if err != nil {
+			os.Remove(dst)
+			err = fmt.Errorf("failed to compress log file: %v", err)
+		}
+	}()
+
+	if _, err := io.Copy(gz, f); err != nil {
+		return err
+	}
+	if err := gz.Close(); err != nil {
+		return err
+	}
+	if err := gzf.Close(); err != nil {
+		return err
+	}
+
+	if err := f.Close(); err != nil {
+		return err
+	}
+	if err := os.Remove(src); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// logInfo is a convenience struct to return the filename and its embedded
+// timestamp.
+type logInfo struct {
+	timestamp time.Time
+	os.FileInfo
+}
+
+// byFormatTime sorts by newest time formatted in the name.
+type byFormatTime []logInfo
+
+func (b byFormatTime) Less(i, j int) bool {
+	return b[i].timestamp.After(b[j].timestamp)
+}
+
+func (b byFormatTime) Swap(i, j int) {
+	b[i], b[j] = b[j], b[i]
+}
+
+func (b byFormatTime) Len() int {
+	return len(b)
+}

+ 816 - 0
pkg/lumberjack/lumberjack_test.go

@@ -0,0 +1,816 @@
+package lumberjack
+
+import (
+	"bytes"
+	"compress/gzip"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"testing"
+	"time"
+
+	"github.com/BurntSushi/toml"
+	"gopkg.in/yaml.v2"
+)
+
+// !!!NOTE!!!
+//
+// Running these tests in parallel will almost certainly cause sporadic (or even
+// regular) failures, because they're all messing with the same global variable
+// that controls the logic's mocked time.Now.  So... don't do that.
+
+// Since all the tests uses the time to determine filenames etc, we need to
+// control the wall clock as much as possible, which means having a wall clock
+// that doesn't change unless we want it to.
+var fakeCurrentTime = time.Now()
+
+func fakeTime() time.Time {
+	return fakeCurrentTime
+}
+
+func TestNewFile(t *testing.T) {
+	currentTime = fakeTime
+
+	dir := makeTempDir("TestNewFile", t)
+	defer os.RemoveAll(dir)
+	l := &Logger{
+		Filename: logFile(dir),
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+	existsWithContent(logFile(dir), b, t)
+	fileCount(dir, 1, t)
+}
+
+func TestOpenExisting(t *testing.T) {
+	currentTime = fakeTime
+	dir := makeTempDir("TestOpenExisting", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	data := []byte("foo!")
+	err := ioutil.WriteFile(filename, data, 0644)
+	isNil(err, t)
+	existsWithContent(filename, data, t)
+
+	l := &Logger{
+		Filename: filename,
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	// make sure the file got appended
+	existsWithContent(filename, append(data, b...), t)
+
+	// make sure no other files were created
+	fileCount(dir, 1, t)
+}
+
+func TestWriteTooLong(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+	dir := makeTempDir("TestWriteTooLong", t)
+	defer os.RemoveAll(dir)
+	l := &Logger{
+		Filename: logFile(dir),
+		MaxSize:  5,
+	}
+	defer l.Close()
+	b := []byte("booooooooooooooo!")
+	n, err := l.Write(b)
+	notNil(err, t)
+	equals(0, n, t)
+	equals(err.Error(),
+		fmt.Sprintf("write length %d exceeds maximum file size %d", len(b), l.MaxSize), t)
+	_, err = os.Stat(logFile(dir))
+	assert(os.IsNotExist(err), t, "File exists, but should not have been created")
+}
+
+func TestMakeLogDir(t *testing.T) {
+	currentTime = fakeTime
+	dir := time.Now().Format("TestMakeLogDir" + backupTimeFormat)
+	dir = filepath.Join(os.TempDir(), dir)
+	defer os.RemoveAll(dir)
+	filename := logFile(dir)
+	l := &Logger{
+		Filename: filename,
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+	existsWithContent(logFile(dir), b, t)
+	fileCount(dir, 1, t)
+}
+
+func TestDefaultFilename(t *testing.T) {
+	currentTime = fakeTime
+	dir := os.TempDir()
+	filename := filepath.Join(dir, filepath.Base(os.Args[0])+"-lumberjack.log")
+	defer os.Remove(filename)
+	l := &Logger{}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+
+	isNil(err, t)
+	equals(len(b), n, t)
+	existsWithContent(filename, b, t)
+}
+
+func TestAutoRotate(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+
+	dir := makeTempDir("TestAutoRotate", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	l := &Logger{
+		Filename: filename,
+		MaxSize:  10,
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	existsWithContent(filename, b, t)
+	fileCount(dir, 1, t)
+
+	newFakeTime()
+
+	b2 := []byte("foooooo!")
+	n, err = l.Write(b2)
+	isNil(err, t)
+	equals(len(b2), n, t)
+
+	// the old logfile should be moved aside and the main logfile should have
+	// only the last write in it.
+	existsWithContent(filename, b2, t)
+
+	// the backup file will use the current fake time and have the old contents.
+	existsWithContent(backupFile(dir), b, t)
+
+	fileCount(dir, 2, t)
+}
+
+func TestFirstWriteRotate(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+	dir := makeTempDir("TestFirstWriteRotate", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	l := &Logger{
+		Filename: filename,
+		MaxSize:  10,
+	}
+	defer l.Close()
+
+	start := []byte("boooooo!")
+	err := ioutil.WriteFile(filename, start, 0600)
+	isNil(err, t)
+
+	newFakeTime()
+
+	// this would make us rotate
+	b := []byte("fooo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	existsWithContent(filename, b, t)
+	existsWithContent(backupFile(dir), start, t)
+
+	fileCount(dir, 2, t)
+}
+
+func TestMaxBackups(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+	dir := makeTempDir("TestMaxBackups", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	l := &Logger{
+		Filename:   filename,
+		MaxSize:    10,
+		MaxBackups: 1,
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	existsWithContent(filename, b, t)
+	fileCount(dir, 1, t)
+
+	newFakeTime()
+
+	// this will put us over the max
+	b2 := []byte("foooooo!")
+	n, err = l.Write(b2)
+	isNil(err, t)
+	equals(len(b2), n, t)
+
+	// this will use the new fake time
+	secondFilename := backupFile(dir)
+	existsWithContent(secondFilename, b, t)
+
+	// make sure the old file still exists with the same content.
+	existsWithContent(filename, b2, t)
+
+	fileCount(dir, 2, t)
+
+	newFakeTime()
+
+	// this will make us rotate again
+	b3 := []byte("baaaaaar!")
+	n, err = l.Write(b3)
+	isNil(err, t)
+	equals(len(b3), n, t)
+
+	// this will use the new fake time
+	thirdFilename := backupFile(dir)
+	existsWithContent(thirdFilename, b2, t)
+
+	existsWithContent(filename, b3, t)
+
+	// we need to wait a little bit since the files get deleted on a different
+	// goroutine.
+	<-time.After(time.Millisecond * 10)
+
+	// should only have two files in the dir still
+	fileCount(dir, 2, t)
+
+	// second file name should still exist
+	existsWithContent(thirdFilename, b2, t)
+
+	// should have deleted the first backup
+	notExist(secondFilename, t)
+
+	// now test that we don't delete directories or non-logfile files
+
+	newFakeTime()
+
+	// create a file that is close to but different from the logfile name.
+	// It shouldn't get caught by our deletion filters.
+	notlogfile := logFile(dir) + ".foo"
+	err = ioutil.WriteFile(notlogfile, []byte("data"), 0644)
+	isNil(err, t)
+
+	// Make a directory that exactly matches our log file filters... it still
+	// shouldn't get caught by the deletion filter since it's a directory.
+	notlogfiledir := backupFile(dir)
+	err = os.Mkdir(notlogfiledir, 0700)
+	isNil(err, t)
+
+	newFakeTime()
+
+	// this will use the new fake time
+	fourthFilename := backupFile(dir)
+
+	// Create a log file that is/was being compressed - this should
+	// not be counted since both the compressed and the uncompressed
+	// log files still exist.
+	compLogFile := fourthFilename + compressSuffix
+	err = ioutil.WriteFile(compLogFile, []byte("compress"), 0644)
+	isNil(err, t)
+
+	// this will make us rotate again
+	b4 := []byte("baaaaaaz!")
+	n, err = l.Write(b4)
+	isNil(err, t)
+	equals(len(b4), n, t)
+
+	existsWithContent(fourthFilename, b3, t)
+	existsWithContent(fourthFilename+compressSuffix, []byte("compress"), t)
+
+	// we need to wait a little bit since the files get deleted on a different
+	// goroutine.
+	<-time.After(time.Millisecond * 10)
+
+	// We should have four things in the directory now - the 2 log files, the
+	// not log file, and the directory
+	fileCount(dir, 5, t)
+
+	// third file name should still exist
+	existsWithContent(filename, b4, t)
+
+	existsWithContent(fourthFilename, b3, t)
+
+	// should have deleted the first filename
+	notExist(thirdFilename, t)
+
+	// the not-a-logfile should still exist
+	exists(notlogfile, t)
+
+	// the directory
+	exists(notlogfiledir, t)
+}
+
+func TestCleanupExistingBackups(t *testing.T) {
+	// test that if we start with more backup files than we're supposed to have
+	// in total, that extra ones get cleaned up when we rotate.
+
+	currentTime = fakeTime
+	megabyte = 1
+
+	dir := makeTempDir("TestCleanupExistingBackups", t)
+	defer os.RemoveAll(dir)
+
+	// make 3 backup files
+
+	data := []byte("data")
+	backup := backupFile(dir)
+	err := ioutil.WriteFile(backup, data, 0644)
+	isNil(err, t)
+
+	newFakeTime()
+
+	backup = backupFile(dir)
+	err = ioutil.WriteFile(backup+compressSuffix, data, 0644)
+	isNil(err, t)
+
+	newFakeTime()
+
+	backup = backupFile(dir)
+	err = ioutil.WriteFile(backup, data, 0644)
+	isNil(err, t)
+
+	// now create a primary log file with some data
+	filename := logFile(dir)
+	err = ioutil.WriteFile(filename, data, 0644)
+	isNil(err, t)
+
+	l := &Logger{
+		Filename:   filename,
+		MaxSize:    10,
+		MaxBackups: 1,
+	}
+	defer l.Close()
+
+	newFakeTime()
+
+	b2 := []byte("foooooo!")
+	n, err := l.Write(b2)
+	isNil(err, t)
+	equals(len(b2), n, t)
+
+	// we need to wait a little bit since the files get deleted on a different
+	// goroutine.
+	<-time.After(time.Millisecond * 10)
+
+	// now we should only have 2 files left - the primary and one backup
+	fileCount(dir, 2, t)
+}
+
+func TestMaxAge(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+
+	dir := makeTempDir("TestMaxAge", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	l := &Logger{
+		Filename: filename,
+		MaxSize:  10,
+		MaxAge:   1,
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	existsWithContent(filename, b, t)
+	fileCount(dir, 1, t)
+
+	// two days later
+	newFakeTime()
+
+	b2 := []byte("foooooo!")
+	n, err = l.Write(b2)
+	isNil(err, t)
+	equals(len(b2), n, t)
+	existsWithContent(backupFile(dir), b, t)
+
+	// we need to wait a little bit since the files get deleted on a different
+	// goroutine.
+	<-time.After(10 * time.Millisecond)
+
+	// We should still have 2 log files, since the most recent backup was just
+	// created.
+	fileCount(dir, 2, t)
+
+	existsWithContent(filename, b2, t)
+
+	// we should have deleted the old file due to being too old
+	existsWithContent(backupFile(dir), b, t)
+
+	// two days later
+	newFakeTime()
+
+	b3 := []byte("baaaaar!")
+	n, err = l.Write(b3)
+	isNil(err, t)
+	equals(len(b3), n, t)
+	existsWithContent(backupFile(dir), b2, t)
+
+	// we need to wait a little bit since the files get deleted on a different
+	// goroutine.
+	<-time.After(10 * time.Millisecond)
+
+	// We should have 2 log files - the main log file, and the most recent
+	// backup.  The earlier backup is past the cutoff and should be gone.
+	fileCount(dir, 2, t)
+
+	existsWithContent(filename, b3, t)
+
+	// we should have deleted the old file due to being too old
+	existsWithContent(backupFile(dir), b2, t)
+}
+
+func TestOldLogFiles(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+
+	dir := makeTempDir("TestOldLogFiles", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	data := []byte("data")
+	err := ioutil.WriteFile(filename, data, 07)
+	isNil(err, t)
+
+	// This gives us a time with the same precision as the time we get from the
+	// timestamp in the name.
+	t1, err := time.Parse(backupTimeFormat, fakeTime().UTC().Format(backupTimeFormat))
+	isNil(err, t)
+
+	backup := backupFile(dir)
+	err = ioutil.WriteFile(backup, data, 07)
+	isNil(err, t)
+
+	newFakeTime()
+
+	t2, err := time.Parse(backupTimeFormat, fakeTime().UTC().Format(backupTimeFormat))
+	isNil(err, t)
+
+	backup2 := backupFile(dir)
+	err = ioutil.WriteFile(backup2, data, 07)
+	isNil(err, t)
+
+	l := &Logger{Filename: filename}
+	files, err := l.oldLogFiles()
+	isNil(err, t)
+	equals(2, len(files), t)
+
+	// should be sorted by newest file first, which would be t2
+	equals(t2, files[0].timestamp, t)
+	equals(t1, files[1].timestamp, t)
+}
+
+func TestTimeFromName(t *testing.T) {
+	l := &Logger{Filename: "/var/log/myfoo/foo.log"}
+	prefix, ext := l.prefixAndExt()
+
+	tests := []struct {
+		filename string
+		want     time.Time
+		wantErr  bool
+	}{
+		{"foo-2014-05-04T14-44-33.555.log", time.Date(2014, 5, 4, 14, 44, 33, 555000000, time.UTC), false},
+		{"foo-2014-05-04T14-44-33.555", time.Time{}, true},
+		{"2014-05-04T14-44-33.555.log", time.Time{}, true},
+		{"foo.log", time.Time{}, true},
+	}
+
+	for _, test := range tests {
+		got, err := l.timeFromName(test.filename, prefix, ext)
+		equals(got, test.want, t)
+		equals(err != nil, test.wantErr, t)
+	}
+}
+
+func TestLocalTime(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+
+	dir := makeTempDir("TestLocalTime", t)
+	defer os.RemoveAll(dir)
+
+	l := &Logger{
+		Filename:  logFile(dir),
+		MaxSize:   10,
+		LocalTime: true,
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	b2 := []byte("fooooooo!")
+	n2, err := l.Write(b2)
+	isNil(err, t)
+	equals(len(b2), n2, t)
+
+	existsWithContent(logFile(dir), b2, t)
+	existsWithContent(backupFileLocal(dir), b, t)
+}
+
+func TestRotate(t *testing.T) {
+	currentTime = fakeTime
+	dir := makeTempDir("TestRotate", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+
+	l := &Logger{
+		Filename:   filename,
+		MaxBackups: 1,
+		MaxSize:    100, // megabytes
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	existsWithContent(filename, b, t)
+	fileCount(dir, 1, t)
+
+	newFakeTime()
+
+	err = l.Rotate()
+	isNil(err, t)
+
+	// we need to wait a little bit since the files get deleted on a different
+	// goroutine.
+	<-time.After(10 * time.Millisecond)
+
+	filename2 := backupFile(dir)
+	existsWithContent(filename2, b, t)
+	existsWithContent(filename, []byte{}, t)
+	fileCount(dir, 2, t)
+	newFakeTime()
+
+	err = l.Rotate()
+	isNil(err, t)
+
+	// we need to wait a little bit since the files get deleted on a different
+	// goroutine.
+	<-time.After(10 * time.Millisecond)
+
+	filename3 := backupFile(dir)
+	existsWithContent(filename3, []byte{}, t)
+	existsWithContent(filename, []byte{}, t)
+	fileCount(dir, 2, t)
+
+	b2 := []byte("foooooo!")
+	n, err = l.Write(b2)
+	isNil(err, t)
+	equals(len(b2), n, t)
+
+	// this will use the new fake time
+	existsWithContent(filename, b2, t)
+}
+
+func TestCompressOnRotate(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+
+	dir := makeTempDir("TestCompressOnRotate", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	l := &Logger{
+		Compress: true,
+		Filename: filename,
+		MaxSize:  10,
+	}
+	defer l.Close()
+	b := []byte("boo!")
+	n, err := l.Write(b)
+	isNil(err, t)
+	equals(len(b), n, t)
+
+	existsWithContent(filename, b, t)
+	fileCount(dir, 1, t)
+
+	newFakeTime()
+
+	err = l.Rotate()
+	isNil(err, t)
+
+	// the old logfile should be moved aside and the main logfile should have
+	// nothing in it.
+	existsWithContent(filename, []byte{}, t)
+
+	// we need to wait a little bit since the files get compressed on a different
+	// goroutine.
+	<-time.After(300 * time.Millisecond)
+
+	// a compressed version of the log file should now exist and the original
+	// should have been removed.
+	bc := new(bytes.Buffer)
+	gz := gzip.NewWriter(bc)
+	_, err = gz.Write(b)
+	isNil(err, t)
+	err = gz.Close()
+	isNil(err, t)
+	existsWithContent(backupFile(dir)+compressSuffix, bc.Bytes(), t)
+	notExist(backupFile(dir), t)
+
+	fileCount(dir, 2, t)
+}
+
+func TestCompressOnResume(t *testing.T) {
+	currentTime = fakeTime
+	megabyte = 1
+
+	dir := makeTempDir("TestCompressOnResume", t)
+	defer os.RemoveAll(dir)
+
+	filename := logFile(dir)
+	l := &Logger{
+		Compress: true,
+		Filename: filename,
+		MaxSize:  10,
+	}
+	defer l.Close()
+
+	// Create a backup file and empty "compressed" file.
+	filename2 := backupFile(dir)
+	b := []byte("foo!")
+	err := ioutil.WriteFile(filename2, b, 0644)
+	isNil(err, t)
+	err = ioutil.WriteFile(filename2+compressSuffix, []byte{}, 0644)
+	isNil(err, t)
+
+	newFakeTime()
+
+	b2 := []byte("boo!")
+	n, err := l.Write(b2)
+	isNil(err, t)
+	equals(len(b2), n, t)
+	existsWithContent(filename, b2, t)
+
+	// we need to wait a little bit since the files get compressed on a different
+	// goroutine.
+	<-time.After(300 * time.Millisecond)
+
+	// The write should have started the compression - a compressed version of
+	// the log file should now exist and the original should have been removed.
+	bc := new(bytes.Buffer)
+	gz := gzip.NewWriter(bc)
+	_, err = gz.Write(b)
+	isNil(err, t)
+	err = gz.Close()
+	isNil(err, t)
+	existsWithContent(filename2+compressSuffix, bc.Bytes(), t)
+	notExist(filename2, t)
+
+	fileCount(dir, 2, t)
+}
+
+func TestJson(t *testing.T) {
+	data := []byte(`
+{
+	"filename": "foo",
+	"maxsize": 5,
+	"maxage": 10,
+	"maxbackups": 3,
+	"localtime": true,
+	"compress": true
+}`[1:])
+
+	l := Logger{}
+	err := json.Unmarshal(data, &l)
+	isNil(err, t)
+	equals("foo", l.Filename, t)
+	equals(5, l.MaxSize, t)
+	equals(10, l.MaxAge, t)
+	equals(3, l.MaxBackups, t)
+	equals(true, l.LocalTime, t)
+	equals(true, l.Compress, t)
+}
+
+func TestYaml(t *testing.T) {
+	data := []byte(`
+filename: foo
+maxsize: 5
+maxage: 10
+maxbackups: 3
+localtime: true
+compress: true`[1:])
+
+	l := Logger{}
+	err := yaml.Unmarshal(data, &l)
+	isNil(err, t)
+	equals("foo", l.Filename, t)
+	equals(5, l.MaxSize, t)
+	equals(10, l.MaxAge, t)
+	equals(3, l.MaxBackups, t)
+	equals(true, l.LocalTime, t)
+	equals(true, l.Compress, t)
+}
+
+func TestToml(t *testing.T) {
+	data := `
+filename = "foo"
+maxsize = 5
+maxage = 10
+maxbackups = 3
+localtime = true
+compress = true`[1:]
+
+	l := Logger{}
+	md, err := toml.Decode(data, &l)
+	isNil(err, t)
+	equals("foo", l.Filename, t)
+	equals(5, l.MaxSize, t)
+	equals(10, l.MaxAge, t)
+	equals(3, l.MaxBackups, t)
+	equals(true, l.LocalTime, t)
+	equals(true, l.Compress, t)
+	equals(0, len(md.Undecoded()), t)
+}
+
+// makeTempDir creates a file with a semi-unique name in the OS temp directory.
+// It should be based on the name of the test, to keep parallel tests from
+// colliding, and must be cleaned up after the test is finished.
+func makeTempDir(name string, t testing.TB) string {
+	dir := time.Now().Format(name + backupTimeFormat)
+	dir = filepath.Join(os.TempDir(), dir)
+	isNilUp(os.Mkdir(dir, 0700), t, 1)
+	return dir
+}
+
+// existsWithContent checks that the given file exists and has the correct content.
+func existsWithContent(path string, content []byte, t testing.TB) {
+	info, err := os.Stat(path)
+	isNilUp(err, t, 1)
+	equalsUp(int64(len(content)), info.Size(), t, 1)
+
+	b, err := ioutil.ReadFile(path)
+	isNilUp(err, t, 1)
+	equalsUp(content, b, t, 1)
+}
+
+// logFile returns the log file name in the given directory for the current fake
+// time.
+func logFile(dir string) string {
+	return filepath.Join(dir, "foobar.log")
+}
+
+func backupFile(dir string) string {
+	return filepath.Join(dir, "foobar-"+fakeTime().UTC().Format(backupTimeFormat)+".log")
+}
+
+func backupFileLocal(dir string) string {
+	return filepath.Join(dir, "foobar-"+fakeTime().Format(backupTimeFormat)+".log")
+}
+
+// logFileLocal returns the log file name in the given directory for the current
+// fake time using the local timezone.
+func logFileLocal(dir string) string {
+	return filepath.Join(dir, fakeTime().Format(backupTimeFormat))
+}
+
+// fileCount checks that the number of files in the directory is exp.
+func fileCount(dir string, exp int, t testing.TB) {
+	files, err := ioutil.ReadDir(dir)
+	isNilUp(err, t, 1)
+	// Make sure no other files were created.
+	equalsUp(exp, len(files), t, 1)
+}
+
+// newFakeTime sets the fake "current time" to two days later.
+func newFakeTime() {
+	fakeCurrentTime = fakeCurrentTime.Add(time.Hour * 24 * 2)
+}
+
+func notExist(path string, t testing.TB) {
+	_, err := os.Stat(path)
+	assertUp(os.IsNotExist(err), t, 1, "expected to get os.IsNotExist, but instead got %v", err)
+}
+
+func exists(path string, t testing.TB) {
+	_, err := os.Stat(path)
+	assertUp(err == nil, t, 1, "expected file to exist, but got error from os.Stat: %v", err)
+}

+ 27 - 0
pkg/lumberjack/rotate_test.go

@@ -0,0 +1,27 @@
+// +build linux
+
+package lumberjack_test
+
+import (
+	"log"
+	"os"
+	"os/signal"
+	"syscall"
+
+	"gopkg.in/natefinch/lumberjack.v2"
+)
+
+// Example of how to rotate in response to SIGHUP.
+func ExampleLogger_Rotate() {
+	l := &lumberjack.Logger{}
+	log.SetOutput(l)
+	c := make(chan os.Signal, 1)
+	signal.Notify(c, syscall.SIGHUP)
+
+	go func() {
+		for {
+			<-c
+			l.Rotate()
+		}
+	}()
+}

+ 91 - 0
pkg/lumberjack/testing_test.go

@@ -0,0 +1,91 @@
+package lumberjack
+
+import (
+	"fmt"
+	"path/filepath"
+	"reflect"
+	"runtime"
+	"testing"
+)
+
+// assert will log the given message if condition is false.
+func assert(condition bool, t testing.TB, msg string, v ...interface{}) {
+	assertUp(condition, t, 1, msg, v...)
+}
+
+// assertUp is like assert, but used inside helper functions, to ensure that
+// the file and line number reported by failures corresponds to one or more
+// levels up the stack.
+func assertUp(condition bool, t testing.TB, caller int, msg string, v ...interface{}) {
+	if !condition {
+		_, file, line, _ := runtime.Caller(caller + 1)
+		v = append([]interface{}{filepath.Base(file), line}, v...)
+		fmt.Printf("%s:%d: "+msg+"\n", v...)
+		t.FailNow()
+	}
+}
+
+// equals tests that the two values are equal according to reflect.DeepEqual.
+func equals(exp, act interface{}, t testing.TB) {
+	equalsUp(exp, act, t, 1)
+}
+
+// equalsUp is like equals, but used inside helper functions, to ensure that the
+// file and line number reported by failures corresponds to one or more levels
+// up the stack.
+func equalsUp(exp, act interface{}, t testing.TB, caller int) {
+	if !reflect.DeepEqual(exp, act) {
+		_, file, line, _ := runtime.Caller(caller + 1)
+		fmt.Printf("%s:%d: exp: %v (%T), got: %v (%T)\n",
+			filepath.Base(file), line, exp, exp, act, act)
+		t.FailNow()
+	}
+}
+
+// isNil reports a failure if the given value is not nil.  Note that values
+// which cannot be nil will always fail this check.
+func isNil(obtained interface{}, t testing.TB) {
+	isNilUp(obtained, t, 1)
+}
+
+// isNilUp is like isNil, but used inside helper functions, to ensure that the
+// file and line number reported by failures corresponds to one or more levels
+// up the stack.
+func isNilUp(obtained interface{}, t testing.TB, caller int) {
+	if !_isNil(obtained) {
+		_, file, line, _ := runtime.Caller(caller + 1)
+		fmt.Printf("%s:%d: expected nil, got: %v\n", filepath.Base(file), line, obtained)
+		t.FailNow()
+	}
+}
+
+// notNil reports a failure if the given value is nil.
+func notNil(obtained interface{}, t testing.TB) {
+	notNilUp(obtained, t, 1)
+}
+
+// notNilUp is like notNil, but used inside helper functions, to ensure that the
+// file and line number reported by failures corresponds to one or more levels
+// up the stack.
+func notNilUp(obtained interface{}, t testing.TB, caller int) {
+	if _isNil(obtained) {
+		_, file, line, _ := runtime.Caller(caller + 1)
+		fmt.Printf("%s:%d: expected non-nil, got: %v\n", filepath.Base(file), line, obtained)
+		t.FailNow()
+	}
+}
+
+// _isNil is a helper function for isNil and notNil, and should not be used
+// directly.
+func _isNil(obtained interface{}) bool {
+	if obtained == nil {
+		return true
+	}
+
+	switch v := reflect.ValueOf(obtained); v.Kind() {
+	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
+		return v.IsNil()
+	}
+
+	return false
+}

+ 18 - 0
proc/proc.go

@@ -2,6 +2,7 @@ package proc
 
 import (
 	"bytes"
+	"fmt"
 	"os"
 	"path"
 	"strconv"
@@ -28,6 +29,23 @@ func GetCmdline(pid uint32) []byte {
 	return bytes.TrimSuffix(cmdline, []byte{0})
 }
 
+func GetRealCmdline(pid uint32) []byte {
+	cmdline, err := os.ReadFile(Path(pid, "cmdline"))
+	if err != nil {
+		return nil
+	}
+	return bytes.ReplaceAll(cmdline, []byte("\x00"), []byte(" "))
+}
+
+func GetExe(pid uint32) []byte {
+	exe, err := os.ReadFile(Path(pid, "exe"))
+	if err != nil {
+		fmt.Printf("Error reading executable path for PID %d: %v\n", pid, err)
+		return nil
+	}
+	return bytes.TrimSuffix(exe, []byte{0})
+}
+
 func GetNsPid(pid uint32) uint32 {
 	data, err := os.ReadFile(Path(pid, "status"))
 	if err != nil {

+ 1 - 1
profiling/profiling.go

@@ -22,7 +22,7 @@ import (
 	"github.com/grafana/pyroscope/ebpf/symtab/elf"
 	"github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/prometheus/model/labels"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 const (

+ 1 - 1
prom/agent.go

@@ -16,7 +16,7 @@ import (
 	"github.com/prometheus/prometheus/storage/remote"
 	"github.com/prometheus/prometheus/tsdb"
 	"github.com/prometheus/prometheus/tsdb/agent"
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 )
 
 const (

+ 1 - 1
prom/wrappers.go

@@ -7,7 +7,7 @@ import (
 	"math"
 	"sync"
 
-	"k8s.io/klog/v2"
+	klog "github.com/sirupsen/logrus"
 
 	"github.com/prometheus/prometheus/config"
 	"github.com/prometheus/prometheus/model/exemplar"

+ 11 - 7
tracing/apm_tracing.go

@@ -3,10 +3,11 @@ package tracing
 import (
 	"context"
 	"fmt"
-	"github.com/coroot/coroot-node-agent/common"
 	"github.com/coroot/coroot-node-agent/ebpftracer"
 	"github.com/coroot/coroot-node-agent/ebpftracer/l7"
 	"github.com/coroot/coroot-node-agent/utils"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
+	klog "github.com/sirupsen/logrus"
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/codes"
 	semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
@@ -120,11 +121,11 @@ func (t *Trace) endReadyEvent(needCount uint32) {
 }
 
 func (t *Trace) AllEventReady(traceID uint64) bool {
-	fmt.Printf("[AllEventReady (current/need)|start|end|traceid](%d/%d)%v|%v|%d\n", *t.currenEventCount, t.needEventCount, t.startEventReady, t.endEventReady, traceID)
+	klog.Infof("[AllEventReady (current/need)|start|end|traceid](%d/%d)%v|%v|%d", *t.currenEventCount, t.needEventCount, t.startEventReady, t.endEventReady, traceID)
 	return t.startEventReady && t.endEventReady && *t.currenEventCount >= t.needEventCount
 }
 
-func (t *Trace) TraceStartEvent(method, path string, status l7.Status, addr netaddr.IPPort, codeType common.CodeType) {
+func (t *Trace) TraceStartEvent(method, path string, status l7.Status, addr netaddr.IPPort, pid uint32, appInfo AppInfo) {
 	t.span.SetAttributes(semconv.HTTPURL(fmt.Sprintf("http://%s%s", addr.String(), path)),
 		semconv.HTTPMethod(method),
 		attribute.String("http.uri", path))
@@ -135,7 +136,13 @@ func (t *Trace) TraceStartEvent(method, path string, status l7.Status, addr neta
 	t.commonAttrs = []attribute.KeyValue{
 		semconv.NetPeerName(addr.IP().String()),
 		semconv.NetPeerPort(int(addr.Port())),
-		attribute.Int("server.code_type", codeType.Int()),
+		// buildAppMapFromEvent
+		attribute.Int("server.code_type", appInfo.CodeType.Int()),
+		attribute.String("server.app_name", appInfo.AppName),
+		attribute.String("server.service_name", appInfo.ServiceName),
+		attribute.Int64("server.app_id", appInfo.AppIdHash.IntVal),
+		attribute.Int64("server.agent_id", appInfo.AgentId),
+		attribute.Int64("server.instance_id", appInfo.InstanceIdHash.IntVal),
 	}
 	t.span.SetAttributes(t.commonAttrs...)
 	t.startReady()
@@ -267,8 +274,6 @@ func (t *Trace) GetSpan() trace.Span {
 }
 
 func (t *Trace) createTraceEvent(name string, eventType int, l7Type int, attrs ...attribute.KeyValue) {
-	fmt.Println("t.currenEventCount", &t.currenEventCount)
-
 	t.span.AddEventApm(name, eventType, l7Type, trace.WithAttributes(attrs...))
 	atomic.AddUint32(t.currenEventCount, 1)
 }
@@ -381,7 +386,6 @@ func (t *Trace) DNSTraceQueryEvent(r *l7.RequestData, _type, fqdn string) {
 		attribute.String("dns.type", _type),
 		attribute.String("dns.fqdn", fqdn),
 	)
-	fmt.Println("DNSTraceQueryEvent")
 	t.appendTimestamp(&attr, r.StartAt, r.EndAt, r.Duration.Nanoseconds())
 	t.createTraceEvent(l7.ProtocolDNS.String()+":"+_type+":"+fqdn, int(ebpftracer.EventTypeL7Request), int(l7.ProtocolDNS), attr...)
 }

+ 3 - 3
tracing/tracing.go

@@ -11,6 +11,7 @@ import (
 	"github.com/coroot/coroot-node-agent/ebpftracer"
 	"github.com/coroot/coroot-node-agent/ebpftracer/l7"
 	"github.com/coroot/coroot-node-agent/flags"
+	klog "github.com/sirupsen/logrus"
 	"go.opentelemetry.io/otel/attribute"
 	"go.opentelemetry.io/otel/codes"
 	"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
@@ -20,7 +21,6 @@ import (
 	semconv "go.opentelemetry.io/otel/semconv/v1.18.0"
 	"go.opentelemetry.io/otel/trace"
 	"inet.af/netaddr"
-	"k8s.io/klog/v2"
 )
 
 const (
@@ -53,7 +53,7 @@ func Init(machineId, hostname, version string) {
 	client := otlptracehttp.NewClient(opts...)
 	exporter, err := otlptrace.New(context.Background(), client)
 	if err != nil {
-		klog.Exitln(err)
+		klog.Fatalln(err)
 	}
 
 	batcher := sdktrace.WithBatcher(exporter)
@@ -80,7 +80,7 @@ type Trace struct {
 	ctx              context.Context
 	span             trace.Span
 	lock             sync.RWMutex
-	stack       []ebpftracer.StackFunEvent
+	stack            []ebpftracer.StackFunEvent
 	currenEventCount *uint32
 	needEventCount   uint32
 	startEventReady  bool

+ 28 - 0
utils/enums/enums.go

@@ -0,0 +1,28 @@
+package enums
+
+import "time"
+
+const (
+	DaemonProc        = "euspace"
+	DaemonCtlProc     = "omniagent-ctl"
+	ServerAgentProc   = "omniagent-os"
+	NpmAgentProc      = "omniagent-net"
+	DaemonServiceFile = "omniagent"
+	CpuLimit          = "omniagent-cpulimit"
+
+	TestApp = "eBPF-APP"
+	// windows
+	DaemonCtlProcEXE   = "omniagent-ctl.exe"
+	ServerAgentProcEXE = "omniagent-os.exe"
+	NpmAgentProcEXE    = "omniagent-net.exe"
+
+	DefaultInterval        = 30
+	DefaultFuseInterval    = 10
+	RootUser               = 0
+	DefaultTimeOut         = time.Duration(10) * time.Minute
+	DefaultLocalProxy      = "http://127.0.0.1"
+	DefaultLocalConfigPort = "18084"
+	DefaultLocalDataPort   = "18085"
+	DefaultLocalFilePort   = "18086"
+	DefaultVpcKey          = "identification"
+)

+ 26 - 159
utils/id.go

@@ -3,6 +3,7 @@ package utils
 import (
 	"crypto/md5"
 	"fmt"
+	. "github.com/coroot/coroot-node-agent/utils/modelse"
 	"math"
 	"os"
 	"path"
@@ -10,38 +11,6 @@ import (
 	"strings"
 )
 
-const (
-	HASH_SIZE_8  = 8
-	HASH_SIZE_16 = 16
-)
-
-// apm_span_context
-type ApmSpanContextT struct {
-	type_from      [1]byte
-	sample         [1]byte
-	host_id        [8]byte
-	app_id         [8]byte
-	instance_id    [8]byte
-	trace_id       [16]byte
-	assumed_app_id [8]byte
-	span_id        [8]byte
-}
-
-type HashByte [HASH_SIZE_8]byte
-
-type HashByte16 [HASH_SIZE_16]byte
-
-type Id_T struct {
-	AppID      ID
-	HostID     ID
-	InstanceID ID
-}
-
-type ID struct {
-	IntVal   int64
-	HashtVal HashByte
-}
-
 func init() {
 
 }
@@ -49,6 +18,7 @@ func init() {
 var (
 	APP_ID_INT64      int64
 	HOST_ID_INT64     int64
+	ACCOUNT_ID_INT    int
 	INSTANCE_ID_INT64 int64
 
 	APP_ID_BYTE      HashByte
@@ -56,38 +26,49 @@ var (
 	INSTANCE_ID_BYTE HashByte
 )
 
-func SetInsID(str string) (int64, HashByte) {
+func BuildInt64ID(str string) ID_STRING {
 	srcCode := md5.Sum([]byte(str))
 	code := fmt.Sprintf("%x", srcCode)
+
 	id_string := md5ToDec(code)
-	fmt.Println(id_string)
-	id, err := strconv.ParseInt(id_string, 10, 64)
-	if err != nil {
-		return 0, HashByte{}
+	return ID_STRING(id_string)
+}
+
+func hexStringToBPFBytes(str string, out *HashByte) {
+	for i := 0; i < len(str)/2; i++ {
+		ch0 := str[2*i]
+		ch1 := str[2*i+1]
+		nib0 := (ch0 & 0x0F) + (ch0 >> 6) | ((ch0 >> 3) & 0x08)
+		nib1 := (ch1 & 0x0F) + (ch1 >> 6) | ((ch1 >> 3) & 0x08)
+		(*out)[i] = (nib0 << 4) | nib1
 	}
-	INSTANCE_ID_INT64 = id
-	var charArray HashByte
-	hexStringToBPFBytes(id_string, &charArray)
-	INSTANCE_ID_BYTE = charArray
-	return id, charArray
 }
 
+// todo account id
+func GetAccountID() int {
+	if ACCOUNT_ID_INT != 0 {
+		return ACCOUNT_ID_INT
+	}
+	ACCOUNT_ID_INT = 110
+	return ACCOUNT_ID_INT
+}
+
+// todo HostId
 func GetHostID() (int64, HashByte) {
 	if HOST_ID_INT64 != 0 {
 		return HOST_ID_INT64, HOST_ID_BYTE
 	}
 	uuid := MachineID()
-	str := fmt.Sprintf("%s:%s", "110", uuid)
+	// todo AccountId
+	str := fmt.Sprintf("%d:%s", GetAccountID(), uuid)
 	srcCode := md5.Sum([]byte(str))
 	code := fmt.Sprintf("%x", srcCode)
 	host_id_string := md5ToDec(code)
-	fmt.Println(host_id_string)
 	id, err := strconv.ParseInt(host_id_string, 10, 64)
 	if err != nil {
 		return 0, HashByte{}
 	}
 
-	fmt.Println(host_id_string)
 	HOST_ID_INT64 = id
 
 	var charArray HashByte
@@ -100,57 +81,6 @@ func GetHostID() (int64, HashByte) {
 	return id, charArray
 }
 
-func GetAppID() (int64, HashByte) {
-	if APP_ID_INT64 != 0 {
-		return APP_ID_INT64, APP_ID_BYTE
-	}
-	//str := fmt.Sprintf("%s:%s", "110", uuid)
-	//srcCode := md5.Sum([]byte(str))
-	//code := fmt.Sprintf("%x", srcCode)
-	//id_string := md5ToDec(code)
-	// todo 应用注册逻辑 && 写入proc_conf层维护
-	var id_string string
-	fmt.Println(os.Getenv("JAVA"))
-	if os.Getenv("JAVA") == "1" {
-		id_string = "3365853273187618"
-	} else {
-		id_string = "5410049101545798"
-	}
-	fmt.Println("APPID:", id_string)
-	id, err := strconv.ParseInt(id_string, 10, 64)
-	if err != nil {
-		return 0, HashByte{}
-	}
-	APP_ID_INT64 = id
-	var charArray HashByte
-	hexStringToBPFBytes(id_string, &charArray)
-	APP_ID_BYTE = charArray
-	return id, charArray
-}
-
-//func GetHostID2() uint64 {
-//	if HOST_ID_UINT64 != 0 {
-//		return HOST_ID_UINT64
-//	}
-//	uuid := MachineID()
-//	hash := make([]byte, HASH_SIZE)
-//	customHash(uuid, hash, HASH_SIZE)
-//
-//	fmt.Print("Hashed string: ")
-//	for i := 0; i < HASH_SIZE; i++ {
-//		fmt.Printf("%d", hash[i])
-//	}
-//	hashInt := byteArrayToInt(hash)
-//	fmt.Println("HashByte as integer:", hashInt)
-//	str := strconv.FormatInt(int64(hashInt), 10)
-//
-//	// 将 hash 转换为字符串(可以使用 Base64 或其他编码)
-//	fmt.Println("HashByte as string:", str)
-//	HOST_ID_UINT64 = hashInt
-//	os.Exit(1)
-//	return hashInt
-//}
-
 func MachineID() string {
 	for _, p := range []string{"sys/devices/virtual/dmi/id/product_uuid", "etc/machine-id", "var/lib/dbus/machine-id"} {
 		payload, err := os.ReadFile(path.Join("/proc/1/root", p))
@@ -163,30 +93,6 @@ func MachineID() string {
 	return ""
 }
 
-func customHash(str string, hash []byte, size int) {
-	var intHash uint64 = 0
-	for i := 0; i < len(str); i++ {
-		intHash = (intHash * 31) + uint64(str[i])
-	}
-
-	// Convert intHash to 16 bytes
-	for i := 0; i < size; i++ {
-		hash[i] = byte(intHash % 10)
-		intHash /= 10
-	}
-	if hash[0] == 0 {
-		hash[0] = 1
-	}
-}
-
-func byteArrayToInt(hash []byte) uint64 {
-	var result uint64
-	for _, b := range hash {
-		result = result*10 + uint64(b)
-	}
-	return result
-}
-
 func hexdec(hexStr string) int {
 	dec, _ := strconv.ParseInt(hexStr, 16, 64)
 	return int(dec)
@@ -202,42 +108,3 @@ func md5ToDec(str string) string {
 	}
 	return strCode
 }
-
-//func hexStringToBPFBytes(str string, size int) []byte {
-//	out := make([]byte, size/2)
-//	for i := 0; i < size/2; i++ {
-//		ch0 := str[2*i]
-//		ch1 := str[2*i+1]
-//		nib0 := (ch0 & 0x0F) + (ch0 >> 6) | ((ch0 >> 3) & 0x08)
-//		nib1 := (ch1 & 0x0F) + (ch1 >> 6) | ((ch1 >> 3) & 0x08)
-//		out[i] = (nib0 << 4) | nib1
-//	}
-//	return out
-//}
-
-func hexStringToBPFBytes(str string, out *HashByte) {
-	for i := 0; i < len(str)/2; i++ {
-		ch0 := str[2*i]
-		ch1 := str[2*i+1]
-		nib0 := (ch0 & 0x0F) + (ch0 >> 6) | ((ch0 >> 3) & 0x08)
-		nib1 := (ch1 & 0x0F) + (ch1 >> 6) | ((ch1 >> 3) & 0x08)
-		(*out)[i] = (nib0 << 4) | nib1
-	}
-}
-
-// hex 表示十六进制字符
-var hex = []byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}
-
-// bytesToHexString 将字节数组转换为十六进制字符串
-func BytesToHexString(pin HashByte) string {
-	size := len(pin)
-	out := make([]byte, size*2)
-	pout := 0
-	for i := 0; i < size; i++ {
-		out[pout] = hex[(pin[i]>>4)&0xF]
-		pout++
-		out[pout] = hex[pin[i]&0xF]
-		pout++
-	}
-	return string(out)
-}

+ 2 - 1
utils/kernel.go

@@ -18,6 +18,7 @@ import (
 	"bufio"
 	"errors"
 	"fmt"
+	klog "github.com/sirupsen/logrus"
 	"golang.org/x/sys/unix"
 	"os"
 	"strconv"
@@ -194,7 +195,6 @@ func GetBootTime() (time.Time, error) {
 	if !bootTime.IsZero() {
 		return bootTime, nil
 	}
-	fmt.Println("GetBootTime")
 	currentTime := time.Now()
 	var info unix.Sysinfo_t
 	if err := unix.Sysinfo(&info); err != nil {
@@ -207,6 +207,7 @@ func GetBootTime() (time.Time, error) {
 func KtimeToTimestamp(t uint64) int64 {
 	bT, err := GetBootTime()
 	if err != nil {
+		klog.Errorf("error getting boot time: %v", err)
 		return int64(0)
 	}
 	return bT.Add(time.Duration(t) * time.Nanosecond).UnixNano()

+ 22 - 0
utils/modelse/app_info.go

@@ -0,0 +1,22 @@
+package modelse
+
+const (
+	APP_NAME    = "app_name"
+	APP_ID      = "app_id"
+	AGENT_ID    = "agent_id"
+	INSTANCE_ID = "instance_id"
+)
+
+type AppInfo struct {
+	AppName        string        `json:"app_name"`
+	AppIdHash      INT_HASH_ID   `json:"app_id_hash"`
+	InstanceIdHash INT_HASH_ID   `json:"instance_id_hash"`
+	AgentId        int64         `json:"agent_id"`
+	Sn             string        `json:"sn"`
+	Sport          int           `json:"sport"`
+	ServiceName    string        `json:"service_name"`
+	CodeType       CodeType      `json:"code_type"`
+	EBPFProcInfo   *EbpfProcInfo `json:"ebpf_proc_info"`
+	RegisterAt     int64         `json:"register_at"`
+	UpdateAt       int64         `json:"update_at"`
+}

+ 9 - 10
ebpftracer/tracer/common.go → utils/modelse/bpf_struct.go

@@ -1,6 +1,4 @@
-package tracer
-
-import "github.com/coroot/coroot-node-agent/utils"
+package modelse
 
 const (
 	ETR_OK               = 0
@@ -86,11 +84,11 @@ const (
 
 var EbpfConfigProtocolFilter [PROTO_NUM]uint32
 
-type testStruct struct {
-	test_id uint32
+type TestStruct struct {
+	Test_id uint32
 }
 
-type bpfOffsetParam struct {
+type BpfOffsetParam struct {
 	Ready                          uint32 `json:"ready"`
 	TaskFilesOffset                uint32 `json:"task__files_offset"`
 	SockFlagsOffset                uint32 `json:"sock__flags_offset"`
@@ -132,7 +130,7 @@ const (
 	OFFSET_IDX_MAX
 )
 
-type traceConf struct {
+type EbpfTraceConf struct {
 	SocketID               uint64 // 会话标识
 	CoroutineTraceID       uint64 // 同一协程的数据转发关联
 	ThreadTraceID          uint64 // 同一进程/线程的数据转发关联,用于多事务流转场景
@@ -141,8 +139,8 @@ type traceConf struct {
 	IOEventCollectMode     uint64
 	IOEventMinimalDuartion uint64
 	TotalCpus              uint64
-	HostID                 utils.HashByte
-	APPID                  utils.HashByte
+	HostID                 HashByte
+	APPID                  HashByte
 }
 
 /*
@@ -181,7 +179,8 @@ type EbpfProcInfo struct {
 	CredentialsSyscallConnItab uint64 // gRPC
 	StartAddr                  uint64
 	EndAddr                    uint64
-	InstanceId                 utils.HashByte
+	InstanceId                 HashByte
+	AppId                      HashByte
 	CodeType                   uint16
 	// go http
 	MethodPtrPos   uint64

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä