2020Example:
2121 ./utils/otel_dd/api_usage_stats_ddog.py --from now-60m --to now > api_usage.csv
2222 ./utils/otel_dd/api_usage_stats_ddog.py --no-skip-e2e | head
23- ./utils/otel_dd/api_usage_stats_ddog.py --from now-24h --to now > api_usage.csv
23+ ./utils/otel_dd/api_usage_stats_ddog.py --from now-24h --to now --env prod > api_usage.csv
2424 ./utils/otel_dd/api_usage_stats_ddog.py --verbose | head
2525"""
2626
3434import re
3535import sys
3636import urllib .error
37+ import urllib .parse
3738import urllib .request
3839from typing import Any , Dict , List , Optional , Tuple
3940
@@ -143,6 +144,24 @@ def is_e2e_true(span: Dict[str, Any]) -> bool:
143144 return str (v ).strip ().lower () == "true"
144145
145146
147+ def api_path_from_candidate (value : Any ) -> Optional [str ]:
148+ if not isinstance (value , str ):
149+ return None
150+ raw = value .strip ()
151+ if not raw :
152+ return None
153+
154+ if raw .startswith ("http://" ) or raw .startswith ("https://" ):
155+ parsed = urllib .parse .urlparse (raw )
156+ path = parsed .path or ""
157+ else :
158+ path = raw .split ("?" , 1 )[0 ]
159+
160+ if not path .startswith ("/v" ):
161+ return None
162+ return path
163+
164+
146165def extract_route (span : Dict [str , Any ], * , sanitize_routes : bool = False ) -> Optional [str ]:
147166 """
148167 Prefer templated HTTP route:
@@ -160,17 +179,28 @@ def extract_route(span: Dict[str, Any], *, sanitize_routes: bool = False) -> Opt
160179 value = route .strip ()
161180 return sanitize_api_path (value ) if sanitize_routes else value
162181
182+ for candidate in (
183+ http .get ("target" ),
184+ http .get ("path" ),
185+ (http .get ("url_details" ) or {}).get ("path" ) if isinstance (http .get ("url_details" ), dict ) else None ,
186+ http .get ("url" ),
187+ ):
188+ path = api_path_from_candidate (candidate )
189+ if path :
190+ return sanitize_api_path (path ) if sanitize_routes else path
191+
163192 resource_name = attrs .get ("resource_name" )
164193 if isinstance (resource_name , str ):
165194 rn = resource_name .strip ()
166195 # Often "METHOD /path"
167196 parts = rn .split (None , 1 )
168197 if len (parts ) == 2 and parts [1 ].startswith ("/" ):
169- value = parts [1 ].strip ()
198+ value = parts [1 ].strip (). split ( "?" , 1 )[ 0 ]
170199 return sanitize_api_path (value ) if sanitize_routes else value
171200 # Sometimes just "/path"
172201 if rn .startswith ("/" ):
173- return sanitize_api_path (rn ) if sanitize_routes else rn
202+ value = rn .split ("?" , 1 )[0 ]
203+ return sanitize_api_path (value ) if sanitize_routes else value
174204
175205 return None
176206
@@ -320,15 +350,19 @@ def main() -> int:
320350
321351 kept = 0
322352 skipped_e2e = 0
353+ skipped_e2e_routes : Dict [str , int ] = {}
323354 skipped_missing_route = 0
324355 skipped_missing_ts = 0
325356
326357 for span in spans :
358+ route = extract_route (span , sanitize_routes = args .sanitize_routes )
359+
327360 if skip_e2e and is_e2e_true (span ):
328361 skipped_e2e += 1
362+ if route :
363+ skipped_e2e_routes [route ] = skipped_e2e_routes .get (route , 0 ) + 1
329364 continue
330365
331- route = extract_route (span , sanitize_routes = args .sanitize_routes )
332366 if not route :
333367 skipped_missing_route += 1
334368 continue
@@ -355,10 +389,16 @@ def main() -> int:
355389 w .writerow ([route , cnt , fmt_ts (tmin ), fmt_ts (tmax )])
356390
357391 if args .verbose :
392+ eprint (f"[ddog] query: { query } " )
393+ eprint (f"[ddog] from/to: { args .time_from } -> { args .time_to } " )
358394 eprint (f"[ddog] spans fetched: { len (spans )} " )
359395 eprint (f"[ddog] spans kept: { kept } " )
360396 if skip_e2e :
361397 eprint (f"[ddog] e2e skipped: { skipped_e2e } " )
398+ if skipped_e2e_routes :
399+ eprint ("[ddog] top skipped e2e routes:" )
400+ for route , cnt in sorted (skipped_e2e_routes .items (), key = lambda x : (- x [1 ], x [0 ]))[:25 ]:
401+ eprint (f"[ddog] { cnt :5d} { route } " )
362402 eprint (f"[ddog] no-route: { skipped_missing_route } " )
363403 eprint (f"[ddog] no-ts: { skipped_missing_ts } " )
364404 eprint (f"[ddog] routes: { len (stats )} " )
0 commit comments