Skip to content

Commit

Permalink
Add helper to manually stop agent processes
Browse files Browse the repository at this point in the history
In some specific contexts such as serverless functions, the execution is
stopped before the agent gets all the data to send it to AppSignal.

There's a new function that manually allows you to gracefully stop
AppSignal's agent processes so all the data is flushed before the
execution is closed.
  • Loading branch information
luismiramirez committed Aug 12, 2024
1 parent 1042266 commit cd581bf
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
bump: patch
type: add
---

New helper to manually stop all agent processes.

Some specific contexts as serverless functions require to ensure all the data is sent to AppSignal before finishing. To achieve this, a new method (`appsignal.stop_agent_processes()`) gracefully stops all AppSignal processes so the data is not lost when wrapping up the app or function execution.
19 changes: 19 additions & 0 deletions src/appsignal/client.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from __future__ import annotations

import os
import signal
import subprocess
import time
from typing import TYPE_CHECKING

from . import internal_logger as logger
Expand Down Expand Up @@ -48,6 +52,21 @@ def start(self) -> None:
else:
logger.info("AppSignal not starting: no active config found")

def stop_agent_processes(self) -> None:
logger.info("Killing all AppSignal agent processes")

processes = subprocess.Popen(["ps", "-A"], stdout=subprocess.PIPE)
out, _ = processes.communicate()
processes_found = 0

for line in out.splitlines():
if b"appsignal-" in line:
pid = int(line.split(None, 1)[0])
os.kill(pid, signal.SIGTERM)
processes_found += 1

logger.info(f"AppSignal stopped {processes_found} agent processes")

def _start_probes(self) -> None:
if self._config.option("enable_minutely_probes"):
start_probes()
24 changes: 24 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import annotations

import os
import signal
from unittest.mock import call, patch

from appsignal.agent import agent
from appsignal.client import Client
Expand Down Expand Up @@ -98,3 +100,25 @@ def test_client_inactive():
os.environ.get("OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST")
is None
)


@patch("subprocess.Popen.communicate")
@patch("os.kill")
def test_client_stop(mock_kill, mock_communicate):
mock_communicate.return_value = (
b"123 ?? 12:00.00 /bin/appsignal-agent\n124 ?? 12:00.00 /foo/appsignal-agent\n",
None,
)
mock_kill.return_value = None

client = Client(active=True, name="MyApp", push_api_key="0000-0000-0000-0000")
client.start()

client.stop_agent_processes()

mock_kill.assert_has_calls(
[
call(123, signal.SIGTERM),
call(124, signal.SIGTERM),
]
)

0 comments on commit cd581bf

Please sign in to comment.