1+ package com .unicorn .store .config ;
2+
3+ import com .fasterxml .jackson .databind .JsonNode ;
4+ import com .fasterxml .jackson .databind .ObjectMapper ;
5+ import io .micrometer .core .instrument .MeterRegistry ;
6+ import io .micrometer .core .instrument .config .MeterFilter ;
7+ import org .springframework .boot .actuate .autoconfigure .metrics .MeterRegistryCustomizer ;
8+ import org .springframework .context .annotation .Bean ;
9+ import org .springframework .context .annotation .Configuration ;
10+
11+ import java .io .File ;
12+ import java .io .IOException ;
13+ import java .net .InetAddress ;
14+ import java .net .URI ;
15+ import java .net .UnknownHostException ;
16+ import java .net .http .HttpClient ;
17+ import java .net .http .HttpRequest ;
18+ import java .net .http .HttpResponse ;
19+ import java .nio .file .Files ;
20+ import java .util .Optional ;
21+
22+ @ Configuration
23+ public class MonitoringConfig {
24+
25+ private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper ();
26+ private static final File NAMESPACE_FILE = new File ("/var/run/secrets/kubernetes.io/serviceaccount/namespace" );
27+
28+ @ Bean
29+ public MeterRegistryCustomizer <MeterRegistry > meterRegistryCustomizer () {
30+ return registry -> {
31+ String clusterType = System .getenv ("ECS_CONTAINER_METADATA_URI_V4" ) != null ? "ecs" : "eks" ;
32+ String cluster = clusterType .equals ("ecs" ) ? extractClusterNameFromMetadata ().orElse ("unknown" ) : Optional .ofNullable (System .getenv ("CLUSTER" )).orElse ("unknown" );
33+ String containerName = "unicorn-store-spring" ;
34+ String taskOrPodId = extractTaskOrPodId ().orElse ("unknown" );
35+ String namespace = clusterType .equals ("eks" ) ? readNamespaceFile ().orElse ("default" ) : "" ;
36+
37+ // Get the container/pod IP address
38+ String ipAddress = getContainerOrPodIp ().orElse ("unknown" );
39+
40+ registry .config ().commonTags (
41+ "cluster" , cluster ,
42+ "cluster_type" , clusterType ,
43+ "container_name" , containerName ,
44+ "task_pod_id" , taskOrPodId ,
45+ "instance" , ipAddress , // Keep this for backward compatibility
46+ "container_ip" , ipAddress // Add this new tag that won't be overwritten
47+ );
48+
49+ if (!namespace .isEmpty ()) {
50+ registry .config ().commonTags ("namespace" , namespace );
51+ } else {
52+ registry .config ().commonTags ("namespace" , "<no namespace>" );
53+ }
54+
55+ registry .config ().meterFilter (
56+ MeterFilter .deny (id ->
57+ id .getName ().equals ("jvm.gc.pause" ) &&
58+ !id .getTags ().stream ().allMatch (tag ->
59+ tag .getKey ().equals ("action" ) ||
60+ tag .getKey ().equals ("cause" ) ||
61+ tag .getKey ().equals ("gc" )
62+ )
63+ )
64+ );
65+ };
66+ }
67+
68+ private Optional <String > extractTaskOrPodId () {
69+ String metadataUri = System .getenv ("ECS_CONTAINER_METADATA_URI_V4" );
70+ if (metadataUri != null ) {
71+ try {
72+ HttpRequest request = HttpRequest .newBuilder ()
73+ .uri (URI .create (metadataUri + "/task" ))
74+ .build ();
75+
76+ HttpResponse <String > response = HttpClient .newHttpClient ()
77+ .send (request , HttpResponse .BodyHandlers .ofString ());
78+
79+ JsonNode root = OBJECT_MAPPER .readTree (response .body ());
80+ String taskArn = root .path ("TaskARN" ).asText ();
81+ String [] parts = taskArn .split ("/" );
82+ return parts .length > 1 ? Optional .of (parts [parts .length - 1 ]) : Optional .empty ();
83+
84+ } catch (IOException | InterruptedException e ) {
85+ return Optional .empty ();
86+ }
87+ }
88+
89+ // EKS fallback: read pod name from Downward API
90+ return readFile ("/etc/podinfo/name" );
91+ }
92+
93+ private Optional <String > extractClusterNameFromMetadata () {
94+ String metadataUri = System .getenv ("ECS_CONTAINER_METADATA_URI_V4" );
95+ if (metadataUri != null ) {
96+ try {
97+ HttpRequest request = HttpRequest .newBuilder ()
98+ .uri (URI .create (metadataUri + "/task" ))
99+ .build ();
100+
101+ HttpResponse <String > response = HttpClient .newHttpClient ()
102+ .send (request , HttpResponse .BodyHandlers .ofString ());
103+
104+ JsonNode root = OBJECT_MAPPER .readTree (response .body ());
105+ String clusterArn = root .path ("Cluster" ).asText ();
106+ String [] parts = clusterArn .split ("/" );
107+ return parts .length > 1 ? Optional .of (parts [parts .length - 1 ]) : Optional .empty ();
108+
109+ } catch (IOException | InterruptedException e ) {
110+ return Optional .empty ();
111+ }
112+ }
113+ return Optional .empty ();
114+ }
115+
116+ private Optional <String > readNamespaceFile () {
117+ return readFile (NAMESPACE_FILE .getAbsolutePath ());
118+ }
119+
120+ private Optional <String > readFile (String path ) {
121+ try {
122+ return Optional .of (Files .readString (new File (path ).toPath ()).trim ());
123+ } catch (IOException e ) {
124+ return Optional .empty ();
125+ }
126+ }
127+
128+ // New method to get the container or pod IP address
129+ private Optional <String > getContainerOrPodIp () {
130+ // For ECS
131+ String metadataUri = System .getenv ("ECS_CONTAINER_METADATA_URI_V4" );
132+ if (metadataUri != null ) {
133+ try {
134+ HttpRequest request = HttpRequest .newBuilder ()
135+ .uri (URI .create (metadataUri ))
136+ .build ();
137+
138+ HttpResponse <String > response = HttpClient .newHttpClient ()
139+ .send (request , HttpResponse .BodyHandlers .ofString ());
140+
141+ JsonNode root = OBJECT_MAPPER .readTree (response .body ());
142+
143+ if (root .has ("Networks" ) && root .path ("Networks" ).isArray () && !root .path ("Networks" ).isEmpty ()) {
144+ JsonNode network = root .path ("Networks" ).get (0 );
145+ if (network .has ("IPv4Addresses" ) && network .path ("IPv4Addresses" ).isArray () &&
146+ !network .path ("IPv4Addresses" ).isEmpty ()) {
147+ return Optional .of (network .path ("IPv4Addresses" ).get (0 ).asText ());
148+ }
149+ }
150+ } catch (IOException | InterruptedException e ) {
151+ // Fall through to next method
152+ }
153+ }
154+
155+ // For Kubernetes/EKS
156+ String podIp = System .getenv ("KUBERNETES_POD_IP" );
157+ if (podIp != null && !podIp .isEmpty ()) {
158+ return Optional .of (podIp );
159+ }
160+
161+ // Try to get local IP as fallback
162+ try {
163+ return Optional .of (InetAddress .getLocalHost ().getHostAddress ());
164+ } catch (UnknownHostException e ) {
165+ return Optional .empty ();
166+ }
167+ }
168+ }
0 commit comments