Skip to content

Commit f1de45b

Browse files
authored
Monitoring API (#68)
* Adding monitoring API * API calls only in 3.12 * API calls only in enterprise
1 parent 1dd2074 commit f1de45b

File tree

4 files changed

+430
-2
lines changed

4 files changed

+430
-2
lines changed

arangoasync/database.py

Lines changed: 342 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88

99
from datetime import datetime
10-
from typing import Any, List, Optional, Sequence, TypeVar, cast
10+
from typing import Any, Dict, List, Optional, Sequence, TypeVar, cast
1111
from warnings import warn
1212

1313
from arangoasync.aql import AQL
@@ -42,6 +42,7 @@
4242
PermissionListError,
4343
PermissionResetError,
4444
PermissionUpdateError,
45+
ServerApiCallsError,
4546
ServerAvailableOptionsGetError,
4647
ServerCheckAvailabilityError,
4748
ServerCurrentOptionsGetError,
@@ -51,8 +52,15 @@
5152
ServerExecuteError,
5253
ServerLicenseGetError,
5354
ServerLicenseSetError,
55+
ServerLogLevelError,
56+
ServerLogLevelResetError,
57+
ServerLogLevelSetError,
58+
ServerLogSettingError,
59+
ServerLogSettingSetError,
60+
ServerMetricsError,
5461
ServerModeError,
5562
ServerModeSetError,
63+
ServerReadLogError,
5664
ServerReloadRoutingError,
5765
ServerShutdownError,
5866
ServerShutdownProgressError,
@@ -2876,6 +2884,339 @@ def response_handler(resp: Response) -> Response:
28762884

28772885
return await self._executor.execute(request, response_handler)
28782886

2887+
async def metrics(self, server_id: Optional[str] = None) -> Result[str]:
2888+
"""Return server metrics in Prometheus format.
2889+
2890+
Args:
2891+
server_id (str | None): Returns metrics of the specified server.
2892+
If no serverId is given, the asked server will reply.
2893+
2894+
Returns:
2895+
str: Server metrics in Prometheus format.
2896+
2897+
Raises:
2898+
ServerMetricsError: If the operation fails.
2899+
2900+
References:
2901+
- `metrics-api-v2 <https://docs.arangodb.com/stable/develop/http-api/monitoring/metrics/#metrics-api-v2>`__
2902+
""" # noqa: E501
2903+
params: Params = {}
2904+
if server_id is not None:
2905+
params["serverId"] = server_id
2906+
2907+
request = Request(
2908+
method=Method.GET,
2909+
endpoint="/_admin/metrics/v2",
2910+
params=params,
2911+
)
2912+
2913+
def response_handler(resp: Response) -> str:
2914+
if not resp.is_success:
2915+
raise ServerMetricsError(resp, request)
2916+
return resp.raw_body.decode("utf-8")
2917+
2918+
return await self._executor.execute(request, response_handler)
2919+
2920+
async def read_log_entries(
2921+
self,
2922+
upto: Optional[int | str] = None,
2923+
level: Optional[str] = None,
2924+
start: Optional[int] = None,
2925+
size: Optional[int] = None,
2926+
offset: Optional[int] = None,
2927+
search: Optional[str] = None,
2928+
sort: Optional[str] = None,
2929+
server_id: Optional[str] = None,
2930+
) -> Result[Json]:
2931+
"""Read the global log from server.
2932+
2933+
Args:
2934+
upto (int | str | None): Return the log entries up to the given level
2935+
(mutually exclusive with parameter **level**). Allowed values are
2936+
"fatal", "error", "warning", "info" (default), "debug" and "trace".
2937+
level (int | str | None): Return the log entries of only the given level
2938+
(mutually exclusive with **upto**).
2939+
start (int | None): Return the log entries whose ID is greater or equal to
2940+
the given value.
2941+
size (int | None): Restrict the size of the result to the given value.
2942+
This can be used for pagination.
2943+
offset (int | None): Number of entries to skip (e.g. for pagination).
2944+
search (str | None): Return only the log entries containing the given text.
2945+
sort (str | None): Sort the log entries according to the given fashion,
2946+
which can be "sort" or "desc".
2947+
server_id (str | None): Returns all log entries of the specified server.
2948+
If no serverId is given, the asked server will reply.
2949+
2950+
Returns:
2951+
dict: Server log entries.
2952+
2953+
Raises:
2954+
ServerReadLogError: If the operation fails.
2955+
2956+
References:
2957+
- `get-the-global-server-logs <https://docs.arangodb.com/stable/develop/http-api/monitoring/logs/#get-the-global-server-logs>`__
2958+
""" # noqa: E501
2959+
params: Params = {}
2960+
if upto is not None:
2961+
params["upto"] = upto
2962+
if level is not None:
2963+
params["level"] = level
2964+
if start is not None:
2965+
params["start"] = start
2966+
if size is not None:
2967+
params["size"] = size
2968+
if offset is not None:
2969+
params["offset"] = offset
2970+
if search is not None:
2971+
params["search"] = search
2972+
if sort is not None:
2973+
params["sort"] = sort
2974+
if server_id is not None:
2975+
params["serverId"] = server_id
2976+
2977+
request = Request(
2978+
method=Method.GET,
2979+
endpoint="/_admin/log/entries",
2980+
params=params,
2981+
prefix_needed=False,
2982+
)
2983+
2984+
def response_handler(resp: Response) -> Json:
2985+
if not resp.is_success:
2986+
raise ServerReadLogError(resp, request)
2987+
2988+
result: Json = self.deserializer.loads(resp.raw_body)
2989+
return result
2990+
2991+
return await self._executor.execute(request, response_handler)
2992+
2993+
async def log_levels(
2994+
self, server_id: Optional[str] = None, with_appenders: Optional[bool] = None
2995+
) -> Result[Json]:
2996+
"""Return current logging levels.
2997+
2998+
Args:
2999+
server_id (str | None): Forward the request to the specified server.
3000+
with_appenders (bool | None): Include appenders in the response.
3001+
3002+
Returns:
3003+
dict: Current logging levels.
3004+
3005+
Raises:
3006+
ServerLogLevelError: If the operation fails.
3007+
3008+
References:
3009+
- `get-the-server-log-levels <https://docs.arangodb.com/stable/develop/http-api/monitoring/logs/#get-the-server-log-levels>`__
3010+
""" # noqa: E501
3011+
params: Params = {}
3012+
if server_id is not None:
3013+
params["serverId"] = server_id
3014+
if with_appenders is not None:
3015+
params["withAppenders"] = with_appenders
3016+
3017+
request = Request(
3018+
method=Method.GET,
3019+
endpoint="/_admin/log/level",
3020+
params=params,
3021+
prefix_needed=False,
3022+
)
3023+
3024+
def response_handler(resp: Response) -> Json:
3025+
if not resp.is_success:
3026+
raise ServerLogLevelError(resp, request)
3027+
result: Json = self.deserializer.loads(resp.raw_body)
3028+
return result
3029+
3030+
return await self._executor.execute(request, response_handler)
3031+
3032+
async def set_log_levels(
3033+
self,
3034+
server_id: Optional[str] = None,
3035+
with_appenders: Optional[bool] = None,
3036+
**kwargs: Dict[str, Any],
3037+
) -> Result[Json]:
3038+
"""Set the logging levels.
3039+
3040+
This method takes arbitrary keyword arguments where the keys are the
3041+
logger names and the values are the logging levels. For example:
3042+
3043+
.. code-block:: python
3044+
3045+
db.set_log_levels(
3046+
agency='DEBUG',
3047+
collector='INFO',
3048+
threads='WARNING'
3049+
)
3050+
3051+
Keys that are not valid logger names are ignored.
3052+
3053+
Args:
3054+
server_id (str | None) -> Forward the request to a specific server.
3055+
with_appenders (bool | None): Include appenders in the response.
3056+
kwargs (dict): Logging levels to be set.
3057+
3058+
Returns:
3059+
dict: New logging levels.
3060+
3061+
Raises:
3062+
ServerLogLevelSetError: If the operation fails.
3063+
3064+
References:
3065+
- `set-the-structured-log-settings <https://docs.arangodb.com/stable/develop/http-api/monitoring/logs/#set-the-structured-log-settings>`__
3066+
""" # noqa: E501
3067+
params: Params = {}
3068+
if server_id is not None:
3069+
params["serverId"] = server_id
3070+
if with_appenders is not None:
3071+
params["withAppenders"] = with_appenders
3072+
3073+
request = Request(
3074+
method=Method.PUT,
3075+
endpoint="/_admin/log/level",
3076+
params=params,
3077+
data=self.serializer.dumps(kwargs),
3078+
prefix_needed=False,
3079+
)
3080+
3081+
def response_handler(resp: Response) -> Json:
3082+
if not resp.is_success:
3083+
raise ServerLogLevelSetError(resp, request)
3084+
result: Json = self.deserializer.loads(resp.raw_body)
3085+
return result
3086+
3087+
return await self._executor.execute(request, response_handler)
3088+
3089+
async def reset_log_levels(self, server_id: Optional[str] = None) -> Result[Json]:
3090+
"""Reset the logging levels.
3091+
3092+
Revert the server’s log level settings to the values they had at startup,
3093+
as determined by the startup options specified on the command-line,
3094+
a configuration file, and the factory defaults.
3095+
3096+
Args:
3097+
server_id: Forward the request to a specific server.
3098+
3099+
Returns:
3100+
dict: New logging levels.
3101+
3102+
Raises:
3103+
ServerLogLevelResetError: If the operation fails.
3104+
3105+
References:
3106+
- `reset-the-server-log-levels <https://docs.arangodb.com/stable/develop/http-api/monitoring/logs/#reset-the-server-log-levels>`__
3107+
""" # noqa: E501
3108+
params: Params = {}
3109+
if server_id is not None:
3110+
params["serverId"] = server_id
3111+
3112+
request = Request(
3113+
method=Method.DELETE,
3114+
endpoint="/_admin/log/level",
3115+
params=params,
3116+
prefix_needed=False,
3117+
)
3118+
3119+
def response_handler(resp: Response) -> Json:
3120+
if not resp.is_success:
3121+
raise ServerLogLevelResetError(resp, request)
3122+
result: Json = self.deserializer.loads(resp.raw_body)
3123+
return result
3124+
3125+
return await self._executor.execute(request, response_handler)
3126+
3127+
async def log_settings(self) -> Result[Json]:
3128+
"""Get the structured log settings.
3129+
3130+
Returns:
3131+
dict: Current structured log settings.
3132+
3133+
Raises:
3134+
ServerLogSettingError: If the operation fails.
3135+
3136+
References:
3137+
- `get-the-structured-log-settings <https://docs.arangodb.com/stable/develop/http-api/monitoring/logs/#get-the-structured-log-settings>`__
3138+
""" # noqa: E501
3139+
request = Request(
3140+
method=Method.GET,
3141+
endpoint="/_admin/log/structured",
3142+
prefix_needed=False,
3143+
)
3144+
3145+
def response_handler(resp: Response) -> Json:
3146+
if not resp.is_success:
3147+
raise ServerLogSettingError(resp, request)
3148+
result: Json = self.deserializer.loads(resp.raw_body)
3149+
return result
3150+
3151+
return await self._executor.execute(request, response_handler)
3152+
3153+
async def set_log_settings(self, **kwargs: Dict[str, Any]) -> Result[Json]:
3154+
"""Set the structured log settings.
3155+
3156+
This method takes arbitrary keyword arguments where the keys are the
3157+
structured log parameters and the values are true or false, for either
3158+
enabling or disabling the parameters.
3159+
3160+
.. code-block:: python
3161+
3162+
db.set_log_settings(
3163+
database=True,
3164+
url=True,
3165+
username=False,
3166+
)
3167+
3168+
Args:
3169+
kwargs (dict): Structured log parameters to be set.
3170+
3171+
Returns:
3172+
dict: New structured log settings.
3173+
3174+
Raises:
3175+
ServerLogSettingSetError: If the operation fails.
3176+
3177+
References:
3178+
- `set-the-structured-log-settings <https://docs.arangodb.com/stable/develop/http-api/monitoring/logs/#set-the-structured-log-settings>`__
3179+
""" # noqa: E501
3180+
request = Request(
3181+
method=Method.PUT,
3182+
endpoint="/_admin/log/structured",
3183+
data=self.serializer.dumps(kwargs),
3184+
prefix_needed=False,
3185+
)
3186+
3187+
def response_handler(resp: Response) -> Json:
3188+
if not resp.is_success:
3189+
raise ServerLogSettingSetError(resp, request)
3190+
result: Json = self.deserializer.loads(resp.raw_body)
3191+
return result
3192+
3193+
return await self._executor.execute(request, response_handler)
3194+
3195+
async def api_calls(self) -> Result[Json]:
3196+
"""Get a list of the most recent requests with a timestamp and the endpoint.
3197+
3198+
Returns:
3199+
dict: API calls made to the server.
3200+
3201+
Raises:
3202+
ServerApiCallsError: If the operation fails.
3203+
3204+
References:
3205+
- `get-recent-api-calls <https://docs.arangodb.com/stable/develop/http-api/monitoring/api-calls/#get-recent-api-calls>`__
3206+
""" # noqa: E501
3207+
request = Request(
3208+
method=Method.GET,
3209+
endpoint="/_admin/server/api-calls",
3210+
)
3211+
3212+
def response_handler(resp: Response) -> Json:
3213+
if not resp.is_success:
3214+
raise ServerApiCallsError(resp, request)
3215+
result: Json = self.deserializer.loads(resp.raw_body)["result"]
3216+
return result
3217+
3218+
return await self._executor.execute(request, response_handler)
3219+
28793220

28803221
class StandardDatabase(Database):
28813222
"""Standard database API wrapper.

0 commit comments

Comments
 (0)