logtap 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
logtap/core/search.py ADDED
@@ -0,0 +1,142 @@
1
+ """
2
+ Search and filtering functionality for logtap.
3
+
4
+ Provides substring, regex-based, and severity-based filtering of log lines.
5
+ """
6
+
7
+ import re
8
+ from typing import List, Optional, Set
9
+
10
+ from logtap.core.parsers.base import LogLevel, ParsedLogEntry
11
+
12
+
13
+ def filter_lines(
14
+ lines: List[str],
15
+ term: Optional[str] = None,
16
+ regex: Optional[str] = None,
17
+ case_sensitive: bool = True,
18
+ ) -> List[str]:
19
+ """
20
+ Filter lines by substring or regex pattern.
21
+
22
+ Args:
23
+ lines: List of log lines to filter.
24
+ term: Substring to search for. If provided, only lines containing
25
+ this term will be returned.
26
+ regex: Regular expression pattern to match. If provided, only lines
27
+ matching this pattern will be returned. Takes precedence over term.
28
+ case_sensitive: Whether the search should be case-sensitive.
29
+ Defaults to True.
30
+
31
+ Returns:
32
+ Filtered list of lines matching the criteria.
33
+ """
34
+ if not term and not regex:
35
+ return lines
36
+
37
+ if regex:
38
+ flags = 0 if case_sensitive else re.IGNORECASE
39
+ try:
40
+ pattern = re.compile(regex, flags)
41
+ return [line for line in lines if pattern.search(line)]
42
+ except re.error:
43
+ # Invalid regex, return empty list
44
+ return []
45
+
46
+ if term:
47
+ if case_sensitive:
48
+ return [line for line in lines if term in line]
49
+ return [line for line in lines if term.lower() in line.lower()]
50
+
51
+ return lines
52
+
53
+
54
+ def filter_by_level(
55
+ entries: List[ParsedLogEntry],
56
+ min_level: Optional[LogLevel] = None,
57
+ levels: Optional[List[LogLevel]] = None,
58
+ ) -> List[ParsedLogEntry]:
59
+ """
60
+ Filter parsed log entries by severity level.
61
+
62
+ Args:
63
+ entries: List of ParsedLogEntry objects to filter.
64
+ min_level: Minimum severity level to include. Entries at this level
65
+ or more severe will be included.
66
+ levels: Specific levels to include. If provided, only entries with
67
+ these exact levels will be returned.
68
+
69
+ Returns:
70
+ Filtered list of ParsedLogEntry objects.
71
+
72
+ Example:
73
+ # Get only ERROR and more severe
74
+ filter_by_level(entries, min_level=LogLevel.ERROR)
75
+
76
+ # Get only WARNING and ERROR
77
+ filter_by_level(entries, levels=[LogLevel.WARNING, LogLevel.ERROR])
78
+ """
79
+ if not min_level and not levels:
80
+ return entries
81
+
82
+ if levels:
83
+ level_set: Set[LogLevel] = set(levels)
84
+ return [e for e in entries if e.level in level_set]
85
+
86
+ if min_level:
87
+ min_severity = min_level.severity
88
+ return [
89
+ e for e in entries
90
+ if e.level is not None and e.level.severity <= min_severity
91
+ ]
92
+
93
+ return entries
94
+
95
+
96
+ def filter_entries(
97
+ entries: List[ParsedLogEntry],
98
+ term: Optional[str] = None,
99
+ regex: Optional[str] = None,
100
+ min_level: Optional[LogLevel] = None,
101
+ levels: Optional[List[LogLevel]] = None,
102
+ case_sensitive: bool = True,
103
+ ) -> List[ParsedLogEntry]:
104
+ """
105
+ Filter parsed log entries by multiple criteria.
106
+
107
+ Combines text search and level filtering.
108
+
109
+ Args:
110
+ entries: List of ParsedLogEntry objects to filter.
111
+ term: Substring to search for in the message.
112
+ regex: Regex pattern to match against the message.
113
+ min_level: Minimum severity level to include.
114
+ levels: Specific levels to include.
115
+ case_sensitive: Whether text search is case-sensitive.
116
+
117
+ Returns:
118
+ Filtered list of ParsedLogEntry objects matching all criteria.
119
+ """
120
+ result = entries
121
+
122
+ # Apply level filter first (usually more restrictive)
123
+ if min_level or levels:
124
+ result = filter_by_level(result, min_level=min_level, levels=levels)
125
+
126
+ # Apply text filter
127
+ if term or regex:
128
+ if regex:
129
+ flags = 0 if case_sensitive else re.IGNORECASE
130
+ try:
131
+ pattern = re.compile(regex, flags)
132
+ result = [e for e in result if pattern.search(e.message)]
133
+ except re.error:
134
+ result = []
135
+ elif term:
136
+ if case_sensitive:
137
+ result = [e for e in result if term in e.message]
138
+ else:
139
+ term_lower = term.lower()
140
+ result = [e for e in result if term_lower in e.message.lower()]
141
+
142
+ return result
@@ -0,0 +1,52 @@
1
+ """
2
+ Input validation functions for logtap.
3
+
4
+ These functions validate user input to prevent security issues
5
+ like path traversal attacks and DoS via overly large inputs.
6
+ """
7
+
8
+
9
+ def is_filename_valid(filename: str) -> bool:
10
+ """
11
+ Validates a filename for path traversal and absolute paths.
12
+
13
+ The filename is considered valid if it does not contain any ".."
14
+ (used for path traversal) and does not start with "/" (indicating an absolute path).
15
+
16
+ Args:
17
+ filename: The filename to be checked.
18
+
19
+ Returns:
20
+ True if filename is valid, False otherwise.
21
+ """
22
+ return ".." not in filename and not filename.startswith("/")
23
+
24
+
25
+ def is_search_term_valid(search_term: str) -> bool:
26
+ """
27
+ Validates a search term based on its length.
28
+
29
+ The search term is considered valid if its length is less than or equal to 100.
30
+
31
+ Args:
32
+ search_term: The search term to be checked.
33
+
34
+ Returns:
35
+ True if search term is valid, False otherwise.
36
+ """
37
+ return len(search_term) <= 100
38
+
39
+
40
+ def is_limit_valid(limit: int) -> bool:
41
+ """
42
+ Validates a limit for the number of lines to return.
43
+
44
+ The limit is considered valid if it's within the range 1 to 1000, inclusive.
45
+
46
+ Args:
47
+ limit: The limit to be checked.
48
+
49
+ Returns:
50
+ True if limit is valid, False otherwise.
51
+ """
52
+ return 1 <= limit <= 1000
@@ -0,0 +1,11 @@
1
+ """Pydantic models for logtap API requests and responses."""
2
+
3
+ from logtap.models.config import Settings
4
+ from logtap.models.responses import ErrorResponse, FileListResponse, LogResponse
5
+
6
+ __all__ = [
7
+ "LogResponse",
8
+ "ErrorResponse",
9
+ "FileListResponse",
10
+ "Settings",
11
+ ]
@@ -0,0 +1,39 @@
1
+ """Configuration settings for logtap."""
2
+
3
+ from typing import Optional
4
+
5
+ from pydantic_settings import BaseSettings, SettingsConfigDict
6
+
7
+
8
+ class Settings(BaseSettings):
9
+ """
10
+ Application settings loaded from environment variables.
11
+
12
+ Environment variables are prefixed with LOGTAP_ (e.g., LOGTAP_API_KEY).
13
+ """
14
+
15
+ model_config = SettingsConfigDict(
16
+ env_prefix="LOGTAP_",
17
+ env_file=".env",
18
+ env_file_encoding="utf-8",
19
+ extra="ignore",
20
+ )
21
+
22
+ # Server settings
23
+ host: str = "0.0.0.0"
24
+ port: int = 8000
25
+
26
+ # Log directory configuration
27
+ log_directory: str = "/var/log"
28
+ testing: bool = False
29
+
30
+ # Authentication
31
+ api_key: Optional[str] = None
32
+
33
+ # Query defaults
34
+ default_limit: int = 50
35
+ max_limit: int = 1000
36
+
37
+ def get_log_directory(self) -> str:
38
+ """Get the log directory. Uses log_directory setting directly."""
39
+ return self.log_directory
@@ -0,0 +1,65 @@
1
+ """Response models for logtap API."""
2
+
3
+ from typing import List, Optional
4
+
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ class LogResponse(BaseModel):
9
+ """Response model for log queries."""
10
+
11
+ lines: List[str] = Field(description="Log lines matching the query")
12
+ count: int = Field(description="Number of lines returned")
13
+ filename: str = Field(description="Name of the log file queried")
14
+
15
+ model_config = {
16
+ "json_schema_extra": {
17
+ "example": {
18
+ "lines": [
19
+ "Jan 8 10:23:45 server sshd[1234]: Accepted publickey",
20
+ "Jan 8 10:23:46 server systemd[1]: Started session",
21
+ ],
22
+ "count": 2,
23
+ "filename": "syslog",
24
+ }
25
+ }
26
+ }
27
+
28
+
29
+ class ErrorResponse(BaseModel):
30
+ """Response model for errors."""
31
+
32
+ error: str = Field(description="Error message")
33
+ detail: Optional[str] = Field(default=None, description="Additional error details")
34
+
35
+ model_config = {
36
+ "json_schema_extra": {
37
+ "example": {
38
+ "error": "File not found",
39
+ "detail": "/var/log/nonexistent does not exist",
40
+ }
41
+ }
42
+ }
43
+
44
+
45
+ class FileListResponse(BaseModel):
46
+ """Response model for listing available log files."""
47
+
48
+ files: List[str] = Field(description="List of available log files")
49
+ directory: str = Field(description="Log directory path")
50
+
51
+ model_config = {
52
+ "json_schema_extra": {
53
+ "example": {
54
+ "files": ["syslog", "auth.log", "kern.log", "dpkg.log"],
55
+ "directory": "/var/log",
56
+ }
57
+ }
58
+ }
59
+
60
+
61
+ class HealthResponse(BaseModel):
62
+ """Response model for health check."""
63
+
64
+ status: str = Field(default="healthy", description="Service status")
65
+ version: str = Field(description="logtap version")
@@ -0,0 +1,317 @@
1
+ Metadata-Version: 2.4
2
+ Name: logtap
3
+ Version: 0.2.0
4
+ Summary: A CLI-first log access tool for Unix systems. Remote log file access without SSH.
5
+ License: GPL-3.0-or-later
6
+ License-File: LICENSE
7
+ Keywords: logs,monitoring,cli,devops,sysadmin
8
+ Author: cainky
9
+ Author-email: kylecain.me@gmail.com
10
+ Requires-Python: >=3.10,<4.0
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
16
+ Classifier: Operating System :: MacOS
17
+ Classifier: Operating System :: POSIX :: Linux
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
+ Classifier: Topic :: System :: Logging
25
+ Classifier: Topic :: System :: Monitoring
26
+ Classifier: Topic :: System :: Systems Administration
27
+ Requires-Dist: aiofiles (>=23.2.1)
28
+ Requires-Dist: fastapi (>=0.109.0)
29
+ Requires-Dist: httpx (>=0.26.0)
30
+ Requires-Dist: pydantic (>=2.5.0)
31
+ Requires-Dist: pydantic-settings (>=2.1.0)
32
+ Requires-Dist: python-dotenv (>=1.0.1)
33
+ Requires-Dist: rich (>=13.7.0)
34
+ Requires-Dist: typer[all] (>=0.9.0)
35
+ Requires-Dist: uvicorn[standard] (>=0.27.0)
36
+ Requires-Dist: websockets (>=12.0)
37
+ Project-URL: Homepage, https://github.com/cainky/logtap
38
+ Project-URL: Repository, https://github.com/cainky/logtap
39
+ Description-Content-Type: text/markdown
40
+
41
+ # logtap
42
+
43
+ [![PyPI version](https://badge.fury.io/py/logtap.svg)](https://badge.fury.io/py/logtap)
44
+ [![Tests](https://github.com/cainky/logtap/actions/workflows/tests.yml/badge.svg)](https://github.com/cainky/logtap/actions/workflows/tests.yml)
45
+ [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
46
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
47
+
48
+ **A CLI-first log access tool for Unix systems. Remote log file access without SSH.**
49
+
50
+ > The simplest way to access log files remotely. No database. No complex setup.
51
+
52
+ ## Features
53
+
54
+ - **Remote Log Access** - Query log files via REST API without SSH
55
+ - **Beautiful CLI** - Colored output with rich formatting
56
+ - **Regex Search** - Powerful filtering with regex patterns
57
+ - **Real-time Streaming** - Follow logs like `tail -f` (WebSocket)
58
+ - **Lightweight** - No database required, minimal dependencies
59
+ - **Secure** - Optional API key authentication
60
+ - **Docker Ready** - One-command deployment
61
+
62
+ ## Quick Start
63
+
64
+ ### Installation
65
+
66
+ ```bash
67
+ pip install logtap
68
+ ```
69
+
70
+ Or with Docker:
71
+
72
+ ```bash
73
+ docker pull cainky/logtap
74
+ ```
75
+
76
+ ### Start the Server
77
+
78
+ On the machine with log files:
79
+
80
+ ```bash
81
+ logtap serve
82
+ ```
83
+
84
+ With authentication:
85
+
86
+ ```bash
87
+ logtap serve --api-key your-secret-key
88
+ ```
89
+
90
+ ### Query Logs
91
+
92
+ From anywhere:
93
+
94
+ ```bash
95
+ # Basic query
96
+ logtap query syslog
97
+
98
+ # Search for errors
99
+ logtap query syslog --term "error"
100
+
101
+ # Regex search
102
+ logtap query auth.log --regex "Failed password.*root"
103
+
104
+ # Last 100 lines
105
+ logtap query syslog --limit 100
106
+
107
+ # From a remote server
108
+ logtap query syslog --server http://myserver:8000 --api-key secret
109
+ ```
110
+
111
+ ### List Available Files
112
+
113
+ ```bash
114
+ logtap files
115
+ ```
116
+
117
+ ### Real-time Streaming
118
+
119
+ ```bash
120
+ logtap tail syslog --follow
121
+ ```
122
+
123
+ ## CLI Commands
124
+
125
+ | Command | Description |
126
+ |---------|-------------|
127
+ | `logtap serve` | Start the API server |
128
+ | `logtap query <file>` | Query log files |
129
+ | `logtap tail <file>` | Tail logs (with `--follow` for streaming) |
130
+ | `logtap files` | List available log files |
131
+
132
+ ### Common Options
133
+
134
+ ```bash
135
+ # Server options
136
+ logtap serve --host 0.0.0.0 --port 8000
137
+ logtap serve --api-key mysecret --log-dir /var/log
138
+
139
+ # Client options
140
+ logtap query syslog --server http://host:8000 --api-key mysecret
141
+ logtap query syslog --term "error" --limit 50
142
+ logtap query syslog --regex "pattern" --ignore-case
143
+ logtap query syslog --output json # json, plain, pretty
144
+ ```
145
+
146
+ ## API Reference
147
+
148
+ ### GET /logs
149
+
150
+ Query log file contents.
151
+
152
+ **Parameters:**
153
+ | Parameter | Type | Default | Description |
154
+ |-----------|------|---------|-------------|
155
+ | `filename` | string | `syslog` | Log file name |
156
+ | `term` | string | - | Substring to search for |
157
+ | `regex` | string | - | Regex pattern to match |
158
+ | `limit` | int | `50` | Number of lines (1-1000) |
159
+ | `case_sensitive` | bool | `true` | Case-sensitive search |
160
+
161
+ **Example:**
162
+ ```bash
163
+ curl "http://localhost:8000/logs?filename=syslog&term=error&limit=10"
164
+ ```
165
+
166
+ **Response:**
167
+ ```json
168
+ {
169
+ "lines": ["Jan 8 10:23:45 server error: connection failed", "..."],
170
+ "count": 10,
171
+ "filename": "syslog"
172
+ }
173
+ ```
174
+
175
+ ### GET /files
176
+
177
+ List available log files.
178
+
179
+ ```bash
180
+ curl "http://localhost:8000/files"
181
+ ```
182
+
183
+ ### GET /health
184
+
185
+ Health check endpoint.
186
+
187
+ ```bash
188
+ curl "http://localhost:8000/health"
189
+ ```
190
+
191
+ ### Authentication
192
+
193
+ If `LOGTAP_API_KEY` is set, all requests require the `X-API-Key` header:
194
+
195
+ ```bash
196
+ curl -H "X-API-Key: your-secret" "http://localhost:8000/logs"
197
+ ```
198
+
199
+ ## Configuration
200
+
201
+ ### Environment Variables
202
+
203
+ | Variable | Default | Description |
204
+ |----------|---------|-------------|
205
+ | `LOGTAP_HOST` | `0.0.0.0` | Server bind host |
206
+ | `LOGTAP_PORT` | `8000` | Server bind port |
207
+ | `LOGTAP_LOG_DIRECTORY` | `/var/log` | Log files directory |
208
+ | `LOGTAP_API_KEY` | - | API key (optional) |
209
+
210
+ ### Using .env File
211
+
212
+ Create a `.env` file:
213
+
214
+ ```env
215
+ LOGTAP_LOG_DIRECTORY=/var/log
216
+ LOGTAP_API_KEY=your-secret-key
217
+ ```
218
+
219
+ ## Docker Deployment
220
+
221
+ ### Using Docker Compose
222
+
223
+ ```yaml
224
+ version: "3.8"
225
+
226
+ services:
227
+ logtap:
228
+ image: cainky/logtap
229
+ ports:
230
+ - "8000:8000"
231
+ volumes:
232
+ - /var/log:/var/log:ro
233
+ environment:
234
+ - LOGTAP_API_KEY=your-secret-key
235
+ ```
236
+
237
+ ```bash
238
+ docker-compose up -d
239
+ ```
240
+
241
+ ### Using Docker Directly
242
+
243
+ ```bash
244
+ docker run -d \
245
+ -p 8000:8000 \
246
+ -v /var/log:/var/log:ro \
247
+ -e LOGTAP_API_KEY=your-secret \
248
+ cainky/logtap
249
+ ```
250
+
251
+ ## Development
252
+
253
+ ### Setup
254
+
255
+ ```bash
256
+ # Clone the repository
257
+ git clone https://github.com/cainky/logtap.git
258
+ cd logtap
259
+
260
+ # Install dependencies
261
+ poetry install
262
+
263
+ # Run tests
264
+ poetry run pytest
265
+
266
+ # Run the server in development mode
267
+ poetry run logtap serve --reload
268
+ ```
269
+
270
+ ### Project Structure
271
+
272
+ ```
273
+ logtap/
274
+ ├── src/logtap/
275
+ │ ├── api/ # FastAPI server
276
+ │ ├── cli/ # Typer CLI commands
277
+ │ ├── core/ # Core business logic
278
+ │ └── models/ # Pydantic models
279
+ ├── tests/
280
+ │ ├── unit/ # Unit tests
281
+ │ └── integration/ # API tests
282
+ ├── Dockerfile
283
+ └── docker-compose.yml
284
+ ```
285
+
286
+ ### Running Tests
287
+
288
+ ```bash
289
+ # All tests
290
+ poetry run pytest
291
+
292
+ # With coverage
293
+ poetry run pytest --cov=logtap
294
+
295
+ # Specific test file
296
+ poetry run pytest tests/unit/test_reader.py
297
+ ```
298
+
299
+ ## Security Considerations
300
+
301
+ - **Path Traversal Protection**: Filenames are validated to prevent `../` attacks
302
+ - **Input Validation**: Search terms limited to 100 chars, limits capped at 1000
303
+ - **Read-Only**: Log directory is mounted read-only in Docker
304
+ - **API Authentication**: Optional API key for production use
305
+
306
+ ## License
307
+
308
+ GPL v3 License - see [LICENSE](LICENSE) for details.
309
+
310
+ ## Contributing
311
+
312
+ Contributions are welcome! Please open an issue to discuss potential changes before submitting a pull request.
313
+
314
+ ## Author
315
+
316
+ Kyle Cain - [@cainky](https://github.com/cainky)
317
+
@@ -0,0 +1,36 @@
1
+ logtap/__init__.py,sha256=AZzOTH4EFgrz3x5g_i6HELBi5Cg8pOXqCWbJbCWnl2A,179
2
+ logtap/__main__.py,sha256=vqJPz3Zf-ICn_4P3B1o7U4NxcCo0qWgfAGEE_j13t-c,138
3
+ logtap/api/__init__.py,sha256=80bP-eIxtAzidgv5nzcfyCEdl8EI2QjVY_eyxjvvhA0,98
4
+ logtap/api/app.py,sha256=BBphxLKvk7yIiLyQ3tdiagqtjc7xFixd_FUzNFjlC0c,1176
5
+ logtap/api/dependencies.py,sha256=1cx1qrp0O6v1fHXA2JdEhC8P4caG2oUSCfMk2-8zmGs,1620
6
+ logtap/api/routes/__init__.py,sha256=XYvFyTP4zKywRZH0v97k0EZCYgxdL2PSUaNet20znPE,29
7
+ logtap/api/routes/files.py,sha256=bqZYrX6jrF5-7GzBpUIXXoPVdxUwm6o0LTcJBLtaJUE,991
8
+ logtap/api/routes/health.py,sha256=Ak-z2ChqZZ7FgHdu1JDo3v5aDBPR3VIICyXTLDBf75E,462
9
+ logtap/api/routes/logs.py,sha256=rIc9pkn7YtVjf0mAluJztVocJ_P9THR280KTEDN1zqE,8431
10
+ logtap/api/routes/parsed.py,sha256=XVvkKBE_hQvfJyrDBBPR_PpVxvof-y4B77xKe9Rr0Qk,3367
11
+ logtap/cli/__init__.py,sha256=U4zaUJ1rm0qHXqeArpzC45S5N-5SBdd8K6foe513msk,31
12
+ logtap/cli/commands/__init__.py,sha256=U4zaUJ1rm0qHXqeArpzC45S5N-5SBdd8K6foe513msk,31
13
+ logtap/cli/commands/files.py,sha256=WFr8kA0SdgQHz3ZyONTaljxHMcD-nQlndp3UIOwZATc,2455
14
+ logtap/cli/commands/query.py,sha256=uD9nH5E-7EqJryLf3hHkDbJSQo4kWFGmzzHgTfAKFwk,3418
15
+ logtap/cli/commands/serve.py,sha256=9OvfII21q6cel3zZfSsAsiERKwKFt0ZFTXmUd2Psthg,1910
16
+ logtap/cli/commands/tail.py,sha256=dwPRXub1dcRwKulDt_qNa2waQm1YPOxIg0QokAK6Gyw,3648
17
+ logtap/cli/main.py,sha256=fWSuQdin9G-RC7Oqzesfp93WZI1-v7227P-WWTsxtIQ,1045
18
+ logtap/core/__init__.py,sha256=tsoL0XuDrPd5xHEu975WqFHoA7EQgloxrum7CjsWHuk,450
19
+ logtap/core/parsers/__init__.py,sha256=5f3hFxf_DgNScRDchRT8ocFVgi7Md4xuMN-ShvlssBo,575
20
+ logtap/core/parsers/apache.py,sha256=JjuQ4v-b7HJvTCcjbOMgv5_dSdiNVPX_EUyplc3f5Qw,5332
21
+ logtap/core/parsers/auto.py,sha256=OLLuX7XIxS0Upnv9FQ-_B0sGAyZmfNxjnMDGdZtUIO4,3565
22
+ logtap/core/parsers/base.py,sha256=HK886mB2mOuRwsVit_U8UgSfrrQOYwMt_XQm-bgso40,4829
23
+ logtap/core/parsers/json_parser.py,sha256=_3uMGT0EW0wwKzXk1Uc9A7iNvey0Ijqr0uRnJuU1_XM,3728
24
+ logtap/core/parsers/nginx.py,sha256=j_oILELOM0azDPLc41wXrLu5o_LhnPs9fT0_iaOqqAQ,3526
25
+ logtap/core/parsers/syslog.py,sha256=gBNQ39QXsigOpfnq3cEdmvFa8NLp_wmiSMDlTt0SIbs,2430
26
+ logtap/core/reader.py,sha256=YY3wyV9NvYiskTh2nrl1tZAZNY11fr33vo85OhW1qHc,5247
27
+ logtap/core/search.py,sha256=-P2KLkjTWANyLQhdxqZc3I-0UpbhWjQQK_yHHxwYJaA,4350
28
+ logtap/core/validation.py,sha256=Nk86jHqEfI4H96fk-1rjbC5sBwfzls43hyOhnRV6rxI,1359
29
+ logtap/models/__init__.py,sha256=tce3Q0QjPhnlAYG8IcwxPedyh1ibBlKIF3CjXe5wwgo,280
30
+ logtap/models/config.py,sha256=8x6OR_y2ZB8SSoQWQGwDB7DXH30UyMNXUcRWOctjUn8,927
31
+ logtap/models/responses.py,sha256=45J-Xw1Gb35uP5188wxy2QZlyy3Fh18fpAFizZnpi3A,1850
32
+ logtap-0.2.0.dist-info/METADATA,sha256=RUTmgmA8QlKTgWT0ch5QPQ21ltJIay2j5SL44_qGBZc,7372
33
+ logtap-0.2.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
34
+ logtap-0.2.0.dist-info/entry_points.txt,sha256=NsN54PsyqB2TA7b8W9-nH4_CNl2_biG4i4FhOQAU9rQ,46
35
+ logtap-0.2.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
36
+ logtap-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.2.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ logtap=logtap.cli.main:app
3
+