feat(go/fio): debug logging via slog at LOG_LEVEL=DEBUG
All checks were successful
Deploy to K8s / deploy (push) Successful in 8s
All checks were successful
Deploy to K8s / deploy (push) Successful in 8s
Wires slog.SetDefault to honour LOG_LEVEL in all CLI commands and adds debug logs on the Fio fetch path so a silent "fetched 0 transaction(s)" can be diagnosed without code changes: - fio.New: which client variant (api/transparent) was selected - apiClient: GET URL (token redacted as ****), HTTP status, body bytes, parsed transaction count - transparentClient: GET URL, HTTP status, body bytes, plus parser stats (raw rows from second table, kept, dropped_bad_date, dropped_nonpositive_amount) Also suppresses the --print-fio-table block when zero transactions were fetched, so the bare header no longer prints under that condition. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
|||||||
"fuj-management/go/internal/services/banksync"
|
"fuj-management/go/internal/services/banksync"
|
||||||
"fuj-management/go/internal/services/membership"
|
"fuj-management/go/internal/services/membership"
|
||||||
"fuj-management/go/internal/web"
|
"fuj-management/go/internal/web"
|
||||||
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -28,6 +29,9 @@ func main() {
|
|||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Honour LOG_LEVEL for slog calls in any package (e.g. internal/io/fio debug logs).
|
||||||
|
slog.SetDefault(logging.New(os.Getenv("LOG_LEVEL")))
|
||||||
|
|
||||||
cmd, args := os.Args[1], os.Args[2:]
|
cmd, args := os.Args[1], os.Args[2:]
|
||||||
|
|
||||||
switch cmd {
|
switch cmd {
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -25,6 +27,9 @@ func (c *apiClient) FetchTransactions(ctx context.Context, from, to time.Time) (
|
|||||||
const layout = "2006-01-02"
|
const layout = "2006-01-02"
|
||||||
url := fmt.Sprintf("https://fioapi.fio.cz/v1/rest/periods/%s/%s/%s/transactions.json",
|
url := fmt.Sprintf("https://fioapi.fio.cz/v1/rest/periods/%s/%s/%s/transactions.json",
|
||||||
c.token, from.Format(layout), to.Format(layout))
|
c.token, from.Format(layout), to.Format(layout))
|
||||||
|
slog.Debug("fio api: GET",
|
||||||
|
"url", strings.Replace(url, c.token, "****", 1),
|
||||||
|
"from", from.Format(layout), "to", to.Format(layout))
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -35,6 +40,7 @@ func (c *apiClient) FetchTransactions(ctx context.Context, from, to time.Time) (
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
slog.Debug("fio api: response", "status", resp.StatusCode)
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, fmt.Errorf("fio api: HTTP %d", resp.StatusCode)
|
return nil, fmt.Errorf("fio api: HTTP %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
@@ -42,7 +48,9 @@ func (c *apiClient) FetchTransactions(ctx context.Context, from, to time.Time) (
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return parseAPIResponse(body)
|
txns, err := parseAPIResponse(body)
|
||||||
|
slog.Debug("fio api: parsed", "body_bytes", len(body), "parsed_count", len(txns))
|
||||||
|
return txns, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// fioAPIResponse is the top-level envelope from the Fio JSON API.
|
// fioAPIResponse is the top-level envelope from the Fio JSON API.
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package fio
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -36,7 +37,9 @@ func New(token, accountNum string, hc httpDoer) Client {
|
|||||||
hc = http.DefaultClient
|
hc = http.DefaultClient
|
||||||
}
|
}
|
||||||
if token != "" {
|
if token != "" {
|
||||||
|
slog.Debug("fio: client selected", "type", "api")
|
||||||
return &apiClient{token: token, hc: hc}
|
return &apiClient{token: token, hc: hc}
|
||||||
}
|
}
|
||||||
|
slog.Debug("fio: client selected", "type", "transparent", "account_num", accountNum)
|
||||||
return &transparentClient{accountNum: accountNum, hc: hc}
|
return &transparentClient{accountNum: accountNum, hc: hc}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -28,6 +29,10 @@ func (c *transparentClient) FetchTransactions(ctx context.Context, from, to time
|
|||||||
from.Format("2.1.2006"),
|
from.Format("2.1.2006"),
|
||||||
to.Format("2.1.2006"),
|
to.Format("2.1.2006"),
|
||||||
)
|
)
|
||||||
|
slog.Debug("fio transparent: GET",
|
||||||
|
"url", url,
|
||||||
|
"from", from.Format("2006-01-02"), "to", to.Format("2006-01-02"))
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -37,6 +42,7 @@ func (c *transparentClient) FetchTransactions(ctx context.Context, from, to time
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
slog.Debug("fio transparent: response", "status", resp.StatusCode)
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, fmt.Errorf("fio transparent: HTTP %d", resp.StatusCode)
|
return nil, fmt.Errorf("fio transparent: HTTP %d", resp.StatusCode)
|
||||||
}
|
}
|
||||||
@@ -44,6 +50,7 @@ func (c *transparentClient) FetchTransactions(ctx context.Context, from, to time
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
slog.Debug("fio transparent: body read", "body_bytes", len(body))
|
||||||
return parseTransparentHTML(body)
|
return parseTransparentHTML(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +70,7 @@ func parseTransparentHTML(body []byte) ([]Transaction, error) {
|
|||||||
rows := extractSecondTableRows(body)
|
rows := extractSecondTableRows(body)
|
||||||
|
|
||||||
var txns []Transaction
|
var txns []Transaction
|
||||||
|
var droppedBadDate, droppedNonpositive int
|
||||||
for _, row := range rows {
|
for _, row := range rows {
|
||||||
col := func(i int) string {
|
col := func(i int) string {
|
||||||
if i < len(row) {
|
if i < len(row) {
|
||||||
@@ -72,7 +80,12 @@ func parseTransparentHTML(body []byte) ([]Transaction, error) {
|
|||||||
}
|
}
|
||||||
dateStr := parseCzechDate(col(tColDate))
|
dateStr := parseCzechDate(col(tColDate))
|
||||||
amount := parseCzechAmount(col(tColAmount))
|
amount := parseCzechAmount(col(tColAmount))
|
||||||
if dateStr == "" || amount <= 0 {
|
if dateStr == "" {
|
||||||
|
droppedBadDate++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if amount <= 0 {
|
||||||
|
droppedNonpositive++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
txns = append(txns, Transaction{
|
txns = append(txns, Transaction{
|
||||||
@@ -86,6 +99,11 @@ func parseTransparentHTML(body []byte) ([]Transaction, error) {
|
|||||||
BankID: "", // not available on HTML path
|
BankID: "", // not available on HTML path
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
slog.Debug("fio transparent: parsed",
|
||||||
|
"raw_rows", len(rows),
|
||||||
|
"kept", len(txns),
|
||||||
|
"dropped_bad_date", droppedBadDate,
|
||||||
|
"dropped_nonpositive_amount", droppedNonpositive)
|
||||||
return txns, nil
|
return txns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,8 +112,8 @@ func SyncToSheets(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4b. Optional debug table (dry-run only).
|
// 4b. Optional debug table (dry-run only; suppress when nothing was fetched).
|
||||||
if opts.DryRun && opts.PrintFioTable {
|
if opts.DryRun && opts.PrintFioTable && len(txns) > 0 {
|
||||||
printFioTable(os.Stdout, txns, syncIDs, existingIDs)
|
printFioTable(os.Stdout, txns, syncIDs, existingIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user