betrieb: Logs je Thema in Tagesdateien (billing, ki, monitoring, market); Doku: Duplicate-Content-Decision durch Anhang 2 abgelöst
- config/logging.php: Themen-Channels billing, ki, monitoring, market als daily-Dateien mit gemeinsamer Aufbewahrung (LOG_DAILY_DAYS), Stack-Default auf daily; Channel `testing` für die Suite. - Fachliche Log-Aufrufe in Billing, KI-Jobs, OperationalAlert und Kursdaten auf Log::channel() umgestellt. - phpunit.xml + TestCase: Suite schreibt ausschließlich nach testing-YYYY-MM-DD.log; LogChannelsTest sichert die Konfiguration. - docs/weiteres: Decision-Update Duplicate-Content entfernt (überholt durch Roadmap-Anhang 2), Decision-Update Verlinkung & Backlinks als Referenz wieder abgelegt, README-Index nachgezogen. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
5122624687
commit
85d7c2b0d5
21 changed files with 338 additions and 107 deletions
|
|
@ -114,7 +114,7 @@ class CheckoutController extends Controller
|
|||
'redirect' => route('me.bookings.index', ['checkout' => 'wechsel']),
|
||||
]);
|
||||
} catch (\Throwable $exception) {
|
||||
Log::error('Tarifwechsel fehlgeschlagen.', [
|
||||
Log::channel('billing')->error('Tarifwechsel fehlgeschlagen.', [
|
||||
'user_id' => $user->id,
|
||||
'plan' => $plan->slug,
|
||||
'interval' => $interval,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class StripeInvoiceRedirectController extends Controller
|
|||
$stripeInvoice = Cashier::stripe()->invoices->retrieve($invoice->stripe_invoice_id);
|
||||
$hostedUrl = $stripeInvoice->hosted_invoice_url;
|
||||
} catch (\Throwable $exception) {
|
||||
Log::warning('Stripe-Rechnungsansicht nicht erreichbar.', [
|
||||
Log::channel('billing')->warning('Stripe-Rechnungsansicht nicht erreichbar.', [
|
||||
'invoice_id' => $invoice->id,
|
||||
'stripe_invoice_id' => $invoice->stripe_invoice_id,
|
||||
'error' => $exception->getMessage(),
|
||||
|
|
|
|||
|
|
@ -165,14 +165,14 @@ class ClassifyPressRelease implements ShouldQueue
|
|||
} catch (StaleClassificationException $exception) {
|
||||
// Inhalt zwischen Audit und Publish-Sperre geändert: publish() hat
|
||||
// bereits eine Prüfung für den aktuellen Stand eingeplant (U02).
|
||||
Log::info('Auto-Veröffentlichung übersprungen: Klassifikation passt nicht mehr zum Inhalt, Prüfung neu eingeplant.', [
|
||||
Log::channel('ki')->info('Auto-Veröffentlichung übersprungen: Klassifikation passt nicht mehr zum Inhalt, Prüfung neu eingeplant.', [
|
||||
'press_release_id' => $pressRelease->id,
|
||||
]);
|
||||
} catch (QuotaExceededException $exception) {
|
||||
// Kontingent zwischen Einreichung und KI-Freigabe aufgebraucht: PM
|
||||
// bleibt in Review, damit Autor/Admin bewusst entscheiden — kein
|
||||
// Job-Retry, der die Klassifikation sinnlos wiederholt.
|
||||
Log::info('Auto-Veröffentlichung übersprungen: PM-Kontingent aufgebraucht, PM bleibt in Review.', [
|
||||
Log::channel('ki')->info('Auto-Veröffentlichung übersprungen: PM-Kontingent aufgebraucht, PM bleibt in Review.', [
|
||||
'press_release_id' => $pressRelease->id,
|
||||
]);
|
||||
}
|
||||
|
|
@ -208,7 +208,7 @@ class ClassifyPressRelease implements ShouldQueue
|
|||
try {
|
||||
return $manager->driver($provider)->classify($pressRelease);
|
||||
} catch (\Throwable $exception) {
|
||||
Log::warning('KI-Klassifikation fiel auf den deterministischen Treiber zurück.', [
|
||||
Log::channel('ki')->warning('KI-Klassifikation fiel auf den deterministischen Treiber zurück.', [
|
||||
'press_release_id' => $pressRelease->id,
|
||||
'provider' => $provider,
|
||||
'error' => $exception->getMessage(),
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ class ScorePressRelease implements ShouldQueue
|
|||
try {
|
||||
return $manager->driver($provider)->score($pressRelease);
|
||||
} catch (\Throwable $exception) {
|
||||
Log::warning('Content-Score fiel auf den deterministischen Treiber zurück.', [
|
||||
Log::channel('ki')->warning('Content-Score fiel auf den deterministischen Treiber zurück.', [
|
||||
'press_release_id' => $pressRelease->id,
|
||||
'provider' => $provider,
|
||||
'error' => $exception->getMessage(),
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ class ProcessStripeWebhook
|
|||
]);
|
||||
|
||||
if ($cleared > 0) {
|
||||
Log::info('Vorgemerkter Tarifwechsel wurde von Stripe vollzogen.', [
|
||||
Log::channel('billing')->info('Vorgemerkter Tarifwechsel wurde von Stripe vollzogen.', [
|
||||
'stripe_subscription_id' => $stripeId,
|
||||
'stripe_price' => $price,
|
||||
]);
|
||||
|
|
@ -217,7 +217,7 @@ class ProcessStripeWebhook
|
|||
$user = Cashier::findBillable($stripeInvoice['customer'] ?? null);
|
||||
|
||||
if (! $user instanceof User) {
|
||||
Log::warning('STR-Spiegelung übersprungen: kein Billable zum Stripe-Customer.', [
|
||||
Log::channel('billing')->warning('STR-Spiegelung übersprungen: kein Billable zum Stripe-Customer.', [
|
||||
'stripe_invoice_id' => $stripeInvoiceId,
|
||||
'stripe_customer' => $stripeInvoice['customer'] ?? null,
|
||||
]);
|
||||
|
|
@ -239,7 +239,7 @@ class ProcessStripeWebhook
|
|||
// (QA-Befund 07.07.: unsigned-Spalten → Webhook-500) — der Beleg
|
||||
// selbst bleibt bei Stripe einsehbar.
|
||||
if ($total <= 0) {
|
||||
Log::info('STR-Spiegelung übersprungen: Stripe-Beleg ohne positiven Rechnungsbetrag (Gutschrift/Null-Rechnung).', [
|
||||
Log::channel('billing')->info('STR-Spiegelung übersprungen: Stripe-Beleg ohne positiven Rechnungsbetrag (Gutschrift/Null-Rechnung).', [
|
||||
'stripe_invoice_id' => $stripeInvoiceId,
|
||||
'total_cents' => $total,
|
||||
]);
|
||||
|
|
@ -286,7 +286,7 @@ class ProcessStripeWebhook
|
|||
return;
|
||||
}
|
||||
|
||||
Log::info('Stripe-Rechnung in den STR-Kreis gespiegelt.', [
|
||||
Log::channel('billing')->info('Stripe-Rechnung in den STR-Kreis gespiegelt.', [
|
||||
'number' => $invoice->number,
|
||||
'stripe_invoice_id' => $stripeInvoiceId,
|
||||
]);
|
||||
|
|
@ -351,7 +351,7 @@ class ProcessStripeWebhook
|
|||
// _failed`. Erfüllt wird deshalb nur bei bestätigter Zahlung; beide
|
||||
// Erfüllungspfade sind idempotent, doppelte Events bleiben folgenlos.
|
||||
if (! in_array($session['payment_status'] ?? null, ['paid', 'no_payment_required'], true)) {
|
||||
Log::info('Checkout-Session noch unbezahlt — Erfüllung wartet auf async_payment_succeeded.', [
|
||||
Log::channel('billing')->info('Checkout-Session noch unbezahlt — Erfüllung wartet auf async_payment_succeeded.', [
|
||||
'session_id' => $session['id'] ?? null,
|
||||
'payment_status' => $session['payment_status'] ?? null,
|
||||
]);
|
||||
|
|
@ -372,7 +372,7 @@ class ProcessStripeWebhook
|
|||
*/
|
||||
private function logFailedAsyncPayment(array $session): void
|
||||
{
|
||||
Log::warning('Asynchrone Stripe-Zahlung fehlgeschlagen — keine Erfüllung.', [
|
||||
Log::channel('billing')->warning('Asynchrone Stripe-Zahlung fehlgeschlagen — keine Erfüllung.', [
|
||||
'session_id' => $session['id'] ?? null,
|
||||
'single_purchase_id' => $session['metadata']['single_purchase_id'] ?? null,
|
||||
'credit_topup_id' => $session['metadata']['credit_topup_id'] ?? null,
|
||||
|
|
@ -402,7 +402,7 @@ class ProcessStripeWebhook
|
|||
$session['payment_intent'] ?? null,
|
||||
);
|
||||
|
||||
Log::info('Wallet-Aufladung gutgeschrieben.', ['credit_topup_id' => $topup->id]);
|
||||
Log::channel('billing')->info('Wallet-Aufladung gutgeschrieben.', ['credit_topup_id' => $topup->id]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -459,7 +459,7 @@ class ProcessStripeWebhook
|
|||
$purchase = $result['purchase'];
|
||||
|
||||
if ($result['marked_paid']) {
|
||||
Log::info('Einmalkauf als bezahlt markiert.', ['single_purchase_id' => $purchase->id]);
|
||||
Log::channel('billing')->info('Einmalkauf als bezahlt markiert.', ['single_purchase_id' => $purchase->id]);
|
||||
|
||||
// Stufe 5 der Konversionsleiter. recordFirstBooking() zählt je
|
||||
// Firma nur den ersten Treffer, deshalb dürfen beide Zahlungspfade
|
||||
|
|
@ -479,7 +479,7 @@ class ProcessStripeWebhook
|
|||
Mail::to($buyer->email)->queue(new PressReleaseChangeApplied($buyer, $changedRelease, $changeRequest));
|
||||
}
|
||||
|
||||
Log::info('Pressemitteilung nach bezahlter Änderung aktualisiert.', [
|
||||
Log::channel('billing')->info('Pressemitteilung nach bezahlter Änderung aktualisiert.', [
|
||||
'single_purchase_id' => $purchase->id,
|
||||
'press_release_id' => $changedRelease->id,
|
||||
]);
|
||||
|
|
@ -497,7 +497,7 @@ class ProcessStripeWebhook
|
|||
Mail::to($buyer->email)->queue(new PressReleaseDepublished($buyer, $pressRelease));
|
||||
}
|
||||
|
||||
Log::info('Pressemitteilung depubliziert.', [
|
||||
Log::channel('billing')->info('Pressemitteilung depubliziert.', [
|
||||
'single_purchase_id' => $purchase->id,
|
||||
'press_release_id' => $pressRelease->id,
|
||||
]);
|
||||
|
|
@ -522,7 +522,7 @@ class ProcessStripeWebhook
|
|||
->first();
|
||||
|
||||
if (! $pressRelease || $pressRelease->status !== PressReleaseStatus::Published) {
|
||||
Log::warning('Depublikation bezahlt, aber PM nicht (mehr) veröffentlicht.', [
|
||||
Log::channel('billing')->warning('Depublikation bezahlt, aber PM nicht (mehr) veröffentlicht.', [
|
||||
'single_purchase_id' => $purchase->id,
|
||||
'press_release_id' => $purchase->press_release_id,
|
||||
]);
|
||||
|
|
@ -568,7 +568,7 @@ class ProcessStripeWebhook
|
|||
->first();
|
||||
|
||||
if (! $pressRelease || $pressRelease->status !== PressReleaseStatus::Published) {
|
||||
Log::warning('Änderung bezahlt, aber PM nicht (mehr) veröffentlicht.', [
|
||||
Log::channel('billing')->warning('Änderung bezahlt, aber PM nicht (mehr) veröffentlicht.', [
|
||||
'single_purchase_id' => $purchase->id,
|
||||
'press_release_id' => $purchase->press_release_id,
|
||||
]);
|
||||
|
|
@ -581,7 +581,7 @@ class ProcessStripeWebhook
|
|||
} catch (CorrectionTooLargeException) {
|
||||
// Kauf bleibt bezahlt/unkonsumiert — Klärung durch den Admin,
|
||||
// statt eine Obergrenzen-Verletzung stillschweigend zu übernehmen.
|
||||
Log::error('Bezahlte Änderung überschreitet die Obergrenze — nicht ausgeführt.', [
|
||||
Log::channel('billing')->error('Bezahlte Änderung überschreitet die Obergrenze — nicht ausgeführt.', [
|
||||
'single_purchase_id' => $purchase->id,
|
||||
'legal_request_id' => $legalRequest->id,
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ class ManualInvoiceService
|
|||
*/
|
||||
private function hold(UserPaymentOption $option, string $reason, array $context): null
|
||||
{
|
||||
Log::warning('MAN-Rechnung übersprungen: '.$reason.'.', $context);
|
||||
Log::channel('billing')->warning('MAN-Rechnung übersprungen: '.$reason.'.', $context);
|
||||
|
||||
$option->forceFill([
|
||||
'billing_hold_reason' => $reason,
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ class StripeCheckoutService
|
|||
try {
|
||||
Cashier::stripe()->subscriptionSchedules->release($subscription->stripe_schedule_id);
|
||||
} catch (InvalidRequestException $exception) {
|
||||
Log::warning('Downgrade-Vormerkung war bei Stripe nicht (mehr) auflösbar.', [
|
||||
Log::channel('billing')->warning('Downgrade-Vormerkung war bei Stripe nicht (mehr) auflösbar.', [
|
||||
'subscription_id' => $subscription->id,
|
||||
'stripe_schedule_id' => $subscription->stripe_schedule_id,
|
||||
'error' => $exception->getMessage(),
|
||||
|
|
@ -241,7 +241,7 @@ class StripeCheckoutService
|
|||
$user->createOrGetStripeCustomer();
|
||||
$user->syncStripeCustomerDetails();
|
||||
} catch (\Throwable $exception) {
|
||||
Log::warning('Stripe-Customer konnte nicht mit der Rechnungsadresse synchronisiert werden.', [
|
||||
Log::channel('billing')->warning('Stripe-Customer konnte nicht mit der Rechnungsadresse synchronisiert werden.', [
|
||||
'user_id' => $user->id,
|
||||
'error' => $exception->getMessage(),
|
||||
]);
|
||||
|
|
@ -299,7 +299,7 @@ class StripeCheckoutService
|
|||
$user->createTaxId('eu_vat', $vatId);
|
||||
}
|
||||
} catch (\Throwable $exception) {
|
||||
Log::warning('USt-ID konnte nicht mit Stripe abgeglichen werden.', [
|
||||
Log::channel('billing')->warning('USt-ID konnte nicht mit Stripe abgeglichen werden.', [
|
||||
'user_id' => $user->id,
|
||||
'error' => $exception->getMessage(),
|
||||
]);
|
||||
|
|
@ -332,7 +332,7 @@ class StripeCheckoutService
|
|||
$check = $this->vatIdValidation->check($vatId, (string) $user->billingAddress?->country_code);
|
||||
|
||||
if (! $check['status']->isUsable()) {
|
||||
Log::warning('USt-ID vor der Buchung nicht bestätigt — sie wird nicht an Stripe übergeben.', [
|
||||
Log::channel('billing')->warning('USt-ID vor der Buchung nicht bestätigt — sie wird nicht an Stripe übergeben.', [
|
||||
'user_id' => $user->id,
|
||||
'vat_id' => $vatId,
|
||||
'status' => $check['status']->value,
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ class VatIdValidationService
|
|||
'angefragteUstid' => $vatId,
|
||||
]);
|
||||
} catch (Throwable $exception) {
|
||||
Log::warning('eVatR-Abfrage fehlgeschlagen.', ['vat_id' => $vatId, 'error' => $exception->getMessage()]);
|
||||
Log::channel('billing')->warning('eVatR-Abfrage fehlgeschlagen.', ['vat_id' => $vatId, 'error' => $exception->getMessage()]);
|
||||
|
||||
return $this->result(
|
||||
VatIdCheckStatus::Unverified,
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class CompanyProfileCheck
|
|||
try {
|
||||
return $this->classification->driver()->classify($proposed);
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('Profilprüfung (Rot-Ebene) fehlgeschlagen — deterministischer Fallback.', [
|
||||
Log::channel('ki')->warning('Profilprüfung (Rot-Ebene) fehlgeschlagen — deterministischer Fallback.', [
|
||||
'company_id' => $company->getKey(),
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class CorrectionService
|
|||
try {
|
||||
return $this->classification->driver()->classify($proposed);
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('KI-Vorprüfung (Spur D) fehlgeschlagen — deterministischer Fallback.', [
|
||||
Log::channel('ki')->warning('KI-Vorprüfung (Spur D) fehlgeschlagen — deterministischer Fallback.', [
|
||||
'press_release_id' => $pressRelease->id,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
|
@ -223,7 +223,7 @@ class CorrectionService
|
|||
}
|
||||
|
||||
if (! $classification['has_changes']) {
|
||||
Log::warning('Bezahlte Änderung ohne wirksame Feld-Änderung — nichts zu tun.', [
|
||||
Log::channel('billing')->warning('Bezahlte Änderung ohne wirksame Feld-Änderung — nichts zu tun.', [
|
||||
'legal_request_id' => $legalRequest->id,
|
||||
'press_release_id' => $pressRelease->id,
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ class MarketDataService
|
|||
}
|
||||
|
||||
if ($slides === []) {
|
||||
Log::warning('market:refresh lieferte keine Kursdaten — bestehender Cache-Stand bleibt erhalten.');
|
||||
Log::channel('market')->warning('market:refresh lieferte keine Kursdaten — bestehender Cache-Stand bleibt erhalten.');
|
||||
|
||||
return $this->slides();
|
||||
}
|
||||
|
|
@ -94,13 +94,13 @@ class MarketDataService
|
|||
'interval' => '1d',
|
||||
]);
|
||||
} catch (Throwable $exception) {
|
||||
Log::warning('Kursabruf fehlgeschlagen.', ['symbol' => $symbol, 'error' => $exception->getMessage()]);
|
||||
Log::channel('market')->warning('Kursabruf fehlgeschlagen.', ['symbol' => $symbol, 'error' => $exception->getMessage()]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $response->successful()) {
|
||||
Log::warning('Kursabruf fehlgeschlagen.', ['symbol' => $symbol, 'status' => $response->status()]);
|
||||
Log::channel('market')->warning('Kursabruf fehlgeschlagen.', ['symbol' => $symbol, 'status' => $response->status()]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class OperationalAlert
|
|||
*/
|
||||
public static function send(string $summary, array $context = []): void
|
||||
{
|
||||
Log::critical('[monitoring] '.$summary, $context);
|
||||
Log::channel('monitoring')->critical('[monitoring] '.$summary, $context);
|
||||
|
||||
if (! config('monitoring.alerts.enabled')) {
|
||||
return;
|
||||
|
|
@ -35,7 +35,7 @@ class OperationalAlert
|
|||
try {
|
||||
Mail::to($recipient)->send(new OperationalAlertMail($summary, $context));
|
||||
} catch (Throwable $e) {
|
||||
Log::error('[monitoring] Alert-Mail konnte nicht versendet werden.', [
|
||||
Log::channel('monitoring')->error('[monitoring] Alert-Mail konnte nicht versendet werden.', [
|
||||
'summary' => $summary,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue