Athento dispone de un método específico para guardar traza de las llamadas a servicios web externos, para ello se usa el modelo OutgoingAPILog , que sirve para:
Registrar todas las llamadas salientes desde Athento o desde operaciones custom a servicios externos (REST APIs, microservicios, integraciones, etc.).
Poder investigar problemas de integración:
Qué usuario provocó la llamada.
Qué endpoint se invocó.
Qué payload se envió.
Qué devolvió el sistema externo y cuánto tardó.
1. Campos principales
username
Usuario Athento o identificador lógico que disparó la operación (p.ej.sap,system,cron_integration).integration
Nombre de la integración o partner. Útil para filtrar en la administración o en reporting (p.ej."SAP","Dynamics","FirmaX").url
URL completa del endpoint externo (https://api.partner.com/v1/contract/123).http_method
Método HTTP (GET,POST, etc.).headers
Cabeceras enviadas en la petición. Normalmente se guardan en formato JSON serializado.payload
Cuerpo de la petición (payload). También suele guardarse como JSON.status_code
Código de respuesta HTTP del servicio externo (200, 400, 500, etc.).
En caso de error de red o timeout puedes usar 0 o algún valor especial.response
Cuerpo de la respuesta del servicio externo. Debe ser texto (puedes guardar JSON, XML, etc.).creation_date
Fecha y hora en la que se creó el log (cuando se preparó la llamada), se completa automáticamente.time_spent
Duración de la llamada en segundos (desde que se hace la petición hasta que se obtiene/produce resultado).info
Campo libre para guardar cualquier dato adicional (IDs externos, trazas resumidas, mensajes de negocio, etc.).
2. Patrón de integración para partners
Los partners que desarrollen operaciones custom sobre Athento deberían seguir este patrón:
Construir la petición (URL, método, headers, payload).
Crear el log antes de llamar:
from audit.models import OutgoingAPILog log = OutgoingAPILog.objects.create( username="usuario_o_servicio", integration="NombrePartner", url=url, http_method="POST", headers=json.dumps(headers), payload=json.dumps(payload), )Ejecutar la llamada con
requests(requests.get,requests.post, etc.) midiendo el tiempo.Actualizar el log con
status_code,responseytime_spent:log.status_code = response.status_code log.response = response.text[:5000] log.time_spent = elapsed log.save()En caso de excepción de
requests, actualizar el log con la información de error y volver a lanzar la excepción.
4. Buenas prácticas
No loggear credenciales sensibles
Antes de hacerjson.dumps(headers_dict)ojson.dumps(payload_dict), elimina o anonmiza campos comoAuthorization,password,token,secret, etc.Limitar tamaño de response/payload
Si esperas respuestas muy grandes, córtalas (por ejemplo, a 5000 caracteres) para no crecer demasiado la base de datos.Usar
integrationconsistentemente
El mismo string para la misma integración facilita mucho filtrar en admin o hacer informes.Usar
time_spentsiempre que sea posible
Te ayudará a detectar servicios externos lentos.No usar "set_feature" como alternativa a esta funcionalidad.
5. Ejemplo completo
A continuación se puede consultar un ejemplo completo:
import json
import time
import requests
from audit.models import ExternalRESTAPILog
def send_invoice_to_partner(invoice_data, user):
"""
Ejemplo de operación que envía una factura a un servicio externo
y registra la llamada en ExternalRESTAPILog.
"""
url = "https://partner.example.com/api/v1/invoices"
http_method = "POST"
# Construimos payload y headers
payload_dict = {
"invoice_number": invoice_data.number,
"amount": str(invoice_data.amount),
"currency": invoice_data.currency,
}
headers_dict = {
"Content-Type": "application/json",
"X-Integration-Key": "NO_LOGGUES_AQUI_TOKENS_REALES", # ver nota abajo
}
# 1) Crear el log ANTES de realizar la llamada
log = ExternalRESTAPILog.objects.create(
username=user.username if user and user.is_authenticated else "system",
integration="PartnerFacturacionX",
url=url,
http_method=http_method,
headers=json.dumps(headers_dict),
payload=json.dumps(payload_dict),
)
# 2) Ejecutar la llamada y medir el tiempo
start = time.monotonic()
try:
response = requests.post(
url,
headers=headers_dict,
json=payload_dict,
timeout=15,
)
elapsed = time.monotonic() - start
# 3) Actualizar el log con la respuesta correcta
log.status_code = response.status_code
# Ojo con respuestas muy grandes: córtalo si hace falta
log.response = response.text[:5000]
log.time_spent = elapsed
log.save()
response.raise_for_status() # opcional, lanza excepción si status >= 400
return response
except requests.RequestException as exc:
elapsed = time.monotonic() - start
# 3b) Actualizar el log con info de error
log.status_code = 0 # puedes usar 0 o un valor especial
log.response = str(exc)
log.time_spent = elapsed
log.info = "Error en la llamada a servicio externo"
log.save()
# Re-lanzar la excepción para que la capa superior decida qué hacer
raise
Comentarios
0 comentarios
Inicie sesión para dejar un comentario.