Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
strategy:
matrix:
python-version:
- "3.9"
- "3.11"
- "3.14"
steps:
- uses: actions/checkout@v7
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ splunk instance using the HTTP Event Collector (HEC).
pip install python-splunk-logging
```

Python 3.9 or newer is required.
Python 3.11 or newer is required.

## Usage

Expand Down
12 changes: 8 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ authors = [
{ name = "Alexander Baker", email = "alexb@splunk.com" },
{ name = "Eric Li", email = "ericli@splunk.com"}
]
requires-python = ">=3.9"
requires-python = ">=3.11"
readme = "README.md"
license = "Apache-2.0"
license-files = ["LICENSE"]
keywords = ["cisco", "splunk", "logging", "http", "hec", "json"]
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"Operating System :: OS Independent",
Expand Down Expand Up @@ -49,19 +53,19 @@ packages = ["splunk_logging"]

[tool.black]
line-length = 120
target-version = ["py39"]
target-version = ["py311"]

[tool.isort]
line_length = 120
profile = "black"

[tool.ruff]
target-version = "py39"
target-version = "py311"
line-length = 120
extend-exclude = ["test"]

[tool.ruff.lint]
select = ["F", "E", "N", "A", "C4", "ISC", "PT", "Q", "RET", "SLF", "ARG", "PTH", "RUF"]
select = ["F", "E", "N", "A", "C4", "ISC", "PT", "Q", "RET", "SLF", "ARG", "PTH", "RUF", "UP"]

[tool.ruff.lint.flake8-quotes]
inline-quotes = "double"
Expand Down
7 changes: 3 additions & 4 deletions splunk_logging/formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,15 @@

import json
import logging
from typing import Optional


class JsonFormatter(logging.Formatter):
def __init__(
self,
fmt: Optional[str] = None,
datefmt: Optional[str] = None,
fmt: str | None = None,
datefmt: str | None = None,
prune_keys: bool = True,
serializer_args: Optional[dict] = None,
serializer_args: dict | None = None,
**default_keys,
):
"""
Expand Down
47 changes: 24 additions & 23 deletions splunk_logging/forwarders.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
import threading
import time
import uuid
from collections.abc import Callable
from dataclasses import dataclass
from datetime import datetime
from typing import Callable, Optional, Union
from typing import Self

import httpx
from dateutil.parser import isoparse
Expand All @@ -36,14 +37,14 @@
)


@dataclass(frozen=True)
@dataclass(frozen=True, slots=True)
class _QueuedEvent:
envelope: dict
payload: str
size: int


@dataclass
@dataclass(slots=True)
class _FlushRequest:
completed: bool = False

Expand All @@ -62,16 +63,16 @@ def __init__(
self,
host: str = "localhost",
port: int = 8088,
token: Optional[str] = None,
token: str | None = None,
use_ssl: bool = True,
verify_ssl: bool = True,
default_host: Optional[str] = None,
default_source: Optional[str] = None,
default_sourcetype: Optional[str] = None,
default_index: Optional[str] = None,
default_host: str | None = None,
default_source: str | None = None,
default_sourcetype: str | None = None,
default_index: str | None = None,
*,
indexer_ack: bool = False,
channel_id: Optional[str] = None,
channel_id: str | None = None,
ack_poll_interval: float = 10.0,
ack_timeout: float = 300.0,
):
Expand Down Expand Up @@ -127,7 +128,7 @@ def __init__(
self._client = self._create_client()
return

def __enter__(self):
def __enter__(self) -> Self:
return self

def __exit__(self, exc_type, exc_value, traceback):
Expand Down Expand Up @@ -167,7 +168,7 @@ def _request(self, method: str, path: str, **kwargs) -> httpx.Response:
time.sleep(delay)
retry_attempt += 1

def _parse_timestamp(self, timeinfo: Union[str, int, float, datetime], timefmt: Optional[str] = None) -> float:
def _parse_timestamp(self, timeinfo: str | int | float | datetime, timefmt: str | None = None) -> float:
"""
Parses various timestamp formats into a float.

Expand Down Expand Up @@ -198,8 +199,8 @@ def _parse_timestamp(self, timeinfo: Union[str, int, float, datetime], timefmt:
def forward_event(
self,
event: dict,
eventtime: Optional[Union[str, int, float, datetime]] = None,
timefmt: Optional[str] = None,
eventtime: str | int | float | datetime | None = None,
timefmt: str | None = None,
**kwargs,
):
"""
Expand Down Expand Up @@ -282,8 +283,8 @@ def _wait_for_acknowledgments(self, ack_ids: list[int]):
def _build_hec_event(
self,
event: dict,
eventtime: Optional[Union[str, int, float, datetime]] = None,
timefmt: Optional[str] = None,
eventtime: str | int | float | datetime | None = None,
timefmt: str | None = None,
**kwargs,
) -> dict:
eventtime = eventtime or datetime.now()
Expand Down Expand Up @@ -311,7 +312,7 @@ def forward_events(
self,
events: list[dict],
eventtime: Callable = lambda _: datetime.now(),
timefmt: Optional[str] = None,
timefmt: str | None = None,
**kwargs,
):
"""
Expand Down Expand Up @@ -384,7 +385,7 @@ def __init__(
flush_interval: float = 2.0,
max_queue_size: int = 10_000,
max_queue_bytes: int = 10_485_760,
enqueue_timeout: Optional[float] = None,
enqueue_timeout: float | None = None,
**kwargs,
):
limits = {
Expand Down Expand Up @@ -423,8 +424,8 @@ def __init__(
def forward_event(
self,
event: dict,
eventtime: Optional[Union[str, int, float, datetime]] = None,
timefmt: Optional[str] = None,
eventtime: str | int | float | datetime | None = None,
timefmt: str | None = None,
**kwargs,
):
queued_event = self._prepare_queued_event(event, eventtime=eventtime, timefmt=timefmt, **kwargs)
Expand All @@ -436,7 +437,7 @@ def forward_events(
self,
events: list[dict],
eventtime: Callable = lambda _: datetime.now(),
timefmt: Optional[str] = None,
timefmt: str | None = None,
**kwargs,
):
deadline = None if self._enqueue_timeout is None else time.monotonic() + self._enqueue_timeout
Expand All @@ -459,7 +460,7 @@ def _enqueue(
self,
queued_event: _QueuedEvent,
*,
deadline: Optional[float],
deadline: float | None,
enqueued_count: int,
next_event_index: int,
):
Expand Down Expand Up @@ -489,8 +490,8 @@ def _enqueue(
def _prepare_queued_event(
self,
event: dict,
eventtime: Optional[Union[str, int, float, datetime]] = None,
timefmt: Optional[str] = None,
eventtime: str | int | float | datetime | None = None,
timefmt: str | None = None,
**kwargs,
) -> _QueuedEvent:
envelope = self._build_hec_event(event, eventtime=eventtime, timefmt=timefmt, **kwargs)
Expand Down
15 changes: 7 additions & 8 deletions splunk_logging/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import logging
import os
import socket
from typing import Optional

from .formatters import JsonFormatter
from .forwarders import BatchHecForwarder, HecForwarder
Expand All @@ -27,17 +26,17 @@ def __init__(
self,
host: str = "localhost",
port: int = 8088,
token: Optional[str] = None,
token: str | None = None,
use_ssl: bool = True,
verify_ssl: bool = True,
default_host: Optional[str] = None,
default_source: Optional[str] = None,
default_sourcetype: Optional[str] = None,
default_index: Optional[str] = None,
default_host: str | None = None,
default_source: str | None = None,
default_sourcetype: str | None = None,
default_index: str | None = None,
ignore_exceptions: bool = True,
*,
indexer_ack: bool = False,
channel_id: Optional[str] = None,
channel_id: str | None = None,
ack_poll_interval: float = 10.0,
ack_timeout: float = 300.0,
batch_enabled: bool = False,
Expand All @@ -46,7 +45,7 @@ def __init__(
flush_interval: float = 2.0,
max_queue_size: int = 10_000,
max_queue_bytes: int = 10_485_760,
enqueue_timeout: Optional[float] = None,
enqueue_timeout: float | None = None,
**kwargs,
):
"""
Expand Down
29 changes: 29 additions & 0 deletions test/test_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copyright 2021 Splunk Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import sys
import unittest
from importlib.metadata import distributions
from pathlib import Path


class TestPackageMetadata(unittest.TestCase):
def test_requires_python_311_or_newer(self):
installed_distribution = next(
distribution
for distribution in distributions(name="python-splunk-logging")
if Path(distribution.locate_file("")).is_relative_to(sys.prefix)
)

self.assertEqual(installed_distribution.metadata["Requires-Python"], ">=3.11")
Loading