Initial release v0.0.1: go-sink - A tool for sending logs to error tracking systems
This commit is contained in:
		
							parent
							
								
									4526d05aef
								
							
						
					
					
						commit
						5c49cadb5e
					
				| 
						 | 
					@ -0,0 +1,22 @@
 | 
				
			||||||
 | 
					# Build Logs
 | 
				
			||||||
 | 
					build_logs/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# OS generated files
 | 
				
			||||||
 | 
					.DS_Store
 | 
				
			||||||
 | 
					.DS_Store?
 | 
				
			||||||
 | 
					._*
 | 
				
			||||||
 | 
					.Spotlight-V100
 | 
				
			||||||
 | 
					.Trashes
 | 
				
			||||||
 | 
					ehthumbs.db
 | 
				
			||||||
 | 
					Thumbs.db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# IDE and editor files
 | 
				
			||||||
 | 
					.cursor/
 | 
				
			||||||
 | 
					.vscode/
 | 
				
			||||||
 | 
					.idea/
 | 
				
			||||||
 | 
					*.swp
 | 
				
			||||||
 | 
					*.swo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Log files
 | 
				
			||||||
 | 
					*.log
 | 
				
			||||||
 | 
					log.json 
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,144 @@
 | 
				
			||||||
 | 
					You can stream and read files to a BugSink DSN, GlitchTip, or Sentry. I was fed up with how brittle sentry-cli was so I made this. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					I might get around to making a whole CI/CD process for this which will upload the executables properly at some point and make this public.
 | 
				
			||||||
 | 
					# Go Sink
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Go Sink is a command-line utility that reads log messages from a file or stdin and sends them to BugSink, GlitchTip, or any compatible error tracking system.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Installation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To install the binary locally, you can use the provided installation script. Run the following command in your terminal:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```sh
 | 
				
			||||||
 | 
					# For the latest stable version
 | 
				
			||||||
 | 
					curl -sSL https://git.nixc.us/Nixius/go-sink/raw/tag/v0.0.1/install.sh | bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Or to install a specific version (replace v0.0.1 with your desired version)
 | 
				
			||||||
 | 
					curl -sSL https://git.nixc.us/Nixius/go-sink/raw/tag/v0.0.1/install.sh | bash
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This will download and install the Go Sink binary to your local machine.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can use Go Sink by specifying a log file as an argument or by piping input to it.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Using a Log File
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```sh
 | 
				
			||||||
 | 
					go-sink /path/to/logfile
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Using Piped Input
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```sh
 | 
				
			||||||
 | 
					cat /path/to/logfile | go-sink
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Server Mode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can also run Go Sink in server mode, which starts a webhook server that accepts log messages via HTTP requests:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```sh
 | 
				
			||||||
 | 
					go-sink server
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The server listens on port 5050 by default. You can send log messages to the webhook endpoint:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```sh
 | 
				
			||||||
 | 
					curl -X POST -H "Content-Type: application/json" \
 | 
				
			||||||
 | 
					  -d '{"log_level": "error", "message": "Error message", "service": "my-service"}' \
 | 
				
			||||||
 | 
					  http://localhost:5050/webhook
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Configuration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Go Sink supports multiple environment variables for the DSN. You can use any of the following:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `BUGSINK_DSN`: For BugSink instances
 | 
				
			||||||
 | 
					- `GLITCHTIP_DSN`: For GlitchTip instances
 | 
				
			||||||
 | 
					- `SENTRY_DSN`: For Sentry instances
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If multiple environment variables are set, they are checked in the order listed above, and the first one found is used.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Setting the DSN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Shell Environment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Add one of the following lines to your `.zshrc` or `.bashrc` file:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```sh
 | 
				
			||||||
 | 
					export BUGSINK_DSN="your-dsn"
 | 
				
			||||||
 | 
					# OR
 | 
				
			||||||
 | 
					export GLITCHTIP_DSN="your-dsn"
 | 
				
			||||||
 | 
					# OR
 | 
				
			||||||
 | 
					export SENTRY_DSN="your-dsn"
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					After adding the line, reload your shell configuration:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```sh
 | 
				
			||||||
 | 
					source ~/.zshrc # for zsh users
 | 
				
			||||||
 | 
					source ~/.bashrc # for bash users
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### Dockerfile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you are using a Docker container, add the environment variable in your `Dockerfile`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```Dockerfile
 | 
				
			||||||
 | 
					ENV BUGSINK_DSN=your-dsn
 | 
				
			||||||
 | 
					# OR
 | 
				
			||||||
 | 
					ENV GLITCHTIP_DSN=your-dsn
 | 
				
			||||||
 | 
					# OR
 | 
				
			||||||
 | 
					ENV SENTRY_DSN=your-dsn
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### docker-compose.yml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you are using Docker Compose, add the environment variable in your `docker-compose.yml` file:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```yaml
 | 
				
			||||||
 | 
					services:
 | 
				
			||||||
 | 
					  go-sink:
 | 
				
			||||||
 | 
					    image: your-docker-image
 | 
				
			||||||
 | 
					    environment:
 | 
				
			||||||
 | 
					      - BUGSINK_DSN=your-dsn
 | 
				
			||||||
 | 
					      # OR
 | 
				
			||||||
 | 
					      # - GLITCHTIP_DSN=your-dsn
 | 
				
			||||||
 | 
					      # OR
 | 
				
			||||||
 | 
					      # - SENTRY_DSN=your-dsn
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Development
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Testing Protocol
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Go Sink includes a comprehensive test suite in `build-run-test.sh` that ensures all functionality works correctly:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. **Build and Verification**: The script builds the application for all supported platforms and verifies that the binaries are created.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2. **Environment Variable Tests**: Each supported DSN environment variable is tested with both plain text and JSON input:
 | 
				
			||||||
 | 
					   - `BUGSINK_DSN`
 | 
				
			||||||
 | 
					   - `GLITCHTIP_DSN`
 | 
				
			||||||
 | 
					   - `SENTRY_DSN`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3. **Priority Tests**: The script tests that when multiple environment variables are set, the correct order of precedence is followed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					4. **Webhook Tests**: The server mode is tested with both single and batch webhook requests.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					To run the tests, simply execute:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```sh
 | 
				
			||||||
 | 
					./build-run-test.sh
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The script provides clear, color-coded output with pass/fail indicators for each test.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Contributing
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Before submitting changes, please ensure that:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. You have run `./build-run-test.sh` to verify that all tests pass
 | 
				
			||||||
 | 
					2. Your changes maintain compatibility with all supported DSN environment variables
 | 
				
			||||||
 | 
					3. Any new features include appropriate tests in the test suite
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,119 @@
 | 
				
			||||||
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Exit on any error
 | 
				
			||||||
 | 
					set -e
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Colors for output
 | 
				
			||||||
 | 
					RED='\033[0;31m'
 | 
				
			||||||
 | 
					GREEN='\033[0;32m'
 | 
				
			||||||
 | 
					YELLOW='\033[0;33m'
 | 
				
			||||||
 | 
					BLUE='\033[0;34m'
 | 
				
			||||||
 | 
					NC='\033[0m' # No Color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Set the DSN
 | 
				
			||||||
 | 
					DSN="https://725ff4678a4b447c8aa22450b64d66ae@bugsink.aenow.com/6"
 | 
				
			||||||
 | 
					echo -e "${BLUE}Testing with multiple environment variable options for DSN${NC}"
 | 
				
			||||||
 | 
					echo "DSN: $DSN"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Function to verify results
 | 
				
			||||||
 | 
					verify_result() {
 | 
				
			||||||
 | 
					    if [ $? -eq 0 ]; then
 | 
				
			||||||
 | 
					        echo -e "${GREEN}✅ Test passed${NC}"
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        echo -e "${RED}❌ Test failed${NC}"
 | 
				
			||||||
 | 
					        exit 1
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Build the application using build.sh
 | 
				
			||||||
 | 
					echo -e "\n${BLUE}Building go-sink using build.sh...${NC}"
 | 
				
			||||||
 | 
					./build.sh
 | 
				
			||||||
 | 
					verify_result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Check if the darwin_arm64 binary exists
 | 
				
			||||||
 | 
					if [ ! -f "./dist/go-sink_darwin_arm64" ]; then
 | 
				
			||||||
 | 
					  echo -e "${RED}❌ Binary for darwin/arm64 not found. Make sure build.sh completed successfully.${NC}"
 | 
				
			||||||
 | 
					  exit 1
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					  echo -e "${GREEN}✅ Binary found${NC}"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Check for running server instances and kill them
 | 
				
			||||||
 | 
					echo -e "\n${BLUE}Checking for existing go-sink server instances...${NC}"
 | 
				
			||||||
 | 
					pkill -f "go-sink.*server" || true
 | 
				
			||||||
 | 
					sleep 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Test 1a: Using GLITCHTIP_DSN with plain text
 | 
				
			||||||
 | 
					echo -e "\n${YELLOW}Test 1a: Using GLITCHTIP_DSN environment variable with plain text...${NC}"
 | 
				
			||||||
 | 
					export GLITCHTIP_DSN="$DSN"
 | 
				
			||||||
 | 
					export SENTRY_DSN=""
 | 
				
			||||||
 | 
					export BUGSINK_DSN=""
 | 
				
			||||||
 | 
					echo "Plain text test with GLITCHTIP_DSN" | ./dist/go-sink_darwin_arm64
 | 
				
			||||||
 | 
					verify_result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Test 1b: Using GLITCHTIP_DSN with JSON
 | 
				
			||||||
 | 
					echo -e "\n${YELLOW}Test 1b: Using GLITCHTIP_DSN environment variable with JSON...${NC}"
 | 
				
			||||||
 | 
					echo '{"log_level": "error", "message": "JSON test with GLITCHTIP_DSN", "service": "test-suite", "test_id": "1b"}' | ./dist/go-sink_darwin_arm64
 | 
				
			||||||
 | 
					verify_result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Test 2a: Using SENTRY_DSN with plain text
 | 
				
			||||||
 | 
					echo -e "\n${YELLOW}Test 2a: Using SENTRY_DSN environment variable with plain text...${NC}"
 | 
				
			||||||
 | 
					export GLITCHTIP_DSN=""
 | 
				
			||||||
 | 
					export SENTRY_DSN="$DSN"
 | 
				
			||||||
 | 
					export BUGSINK_DSN=""
 | 
				
			||||||
 | 
					echo "Plain text test with SENTRY_DSN" | ./dist/go-sink_darwin_arm64
 | 
				
			||||||
 | 
					verify_result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Test 2b: Using SENTRY_DSN with JSON
 | 
				
			||||||
 | 
					echo -e "\n${YELLOW}Test 2b: Using SENTRY_DSN environment variable with JSON...${NC}"
 | 
				
			||||||
 | 
					echo '{"log_level": "error", "message": "JSON test with SENTRY_DSN", "service": "test-suite", "test_id": "2b"}' | ./dist/go-sink_darwin_arm64
 | 
				
			||||||
 | 
					verify_result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Test 3a: Using BUGSINK_DSN with plain text
 | 
				
			||||||
 | 
					echo -e "\n${YELLOW}Test 3a: Using BUGSINK_DSN environment variable with plain text...${NC}"
 | 
				
			||||||
 | 
					export GLITCHTIP_DSN=""
 | 
				
			||||||
 | 
					export SENTRY_DSN=""
 | 
				
			||||||
 | 
					export BUGSINK_DSN="$DSN"
 | 
				
			||||||
 | 
					echo "Plain text test with BUGSINK_DSN" | ./dist/go-sink_darwin_arm64
 | 
				
			||||||
 | 
					verify_result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Test 3b: Using BUGSINK_DSN with JSON
 | 
				
			||||||
 | 
					echo -e "\n${YELLOW}Test 3b: Using BUGSINK_DSN environment variable with JSON...${NC}"
 | 
				
			||||||
 | 
					echo '{"log_level": "error", "message": "JSON test with BUGSINK_DSN", "service": "test-suite", "test_id": "3b"}' | ./dist/go-sink_darwin_arm64
 | 
				
			||||||
 | 
					verify_result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Test environment variable priority (GLITCHTIP_DSN should take precedence)
 | 
				
			||||||
 | 
					echo -e "\n${YELLOW}Test 4: Testing environment variable priority...${NC}"
 | 
				
			||||||
 | 
					export GLITCHTIP_DSN="$DSN"
 | 
				
			||||||
 | 
					export SENTRY_DSN="$DSN"
 | 
				
			||||||
 | 
					export BUGSINK_DSN="$DSN"
 | 
				
			||||||
 | 
					echo "Testing environment variable priority" | ./dist/go-sink_darwin_arm64
 | 
				
			||||||
 | 
					verify_result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Start the server with GLITCHTIP_DSN for webhook tests
 | 
				
			||||||
 | 
					echo -e "\n${BLUE}Starting go-sink server with GLITCHTIP_DSN...${NC}"
 | 
				
			||||||
 | 
					export GLITCHTIP_DSN="$DSN"
 | 
				
			||||||
 | 
					export SENTRY_DSN=""
 | 
				
			||||||
 | 
					export BUGSINK_DSN=""
 | 
				
			||||||
 | 
					./dist/go-sink_darwin_arm64 server &
 | 
				
			||||||
 | 
					SERVER_PID=$!
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Give the server time to start
 | 
				
			||||||
 | 
					echo "Waiting for server to start..."
 | 
				
			||||||
 | 
					sleep 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Test 5: Send webhook request
 | 
				
			||||||
 | 
					echo -e "\n${YELLOW}Test 5: Sending a webhook request...${NC}"
 | 
				
			||||||
 | 
					curl -X POST -H "Content-Type: application/json" -d '{"log_level": "error", "message": "Error via webhook", "service": "test-suite", "test_id": "5"}' http://localhost:5050/webhook
 | 
				
			||||||
 | 
					verify_result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Test 6: Send webhook batch request
 | 
				
			||||||
 | 
					echo -e "\n${YELLOW}Test 6: Sending a webhook batch request...${NC}"
 | 
				
			||||||
 | 
					curl -X POST -H "Content-Type: application/json" -d '[{"log_level": "error", "message": "Error in batch", "service": "test-suite", "test_id": "6.1"},{"log_level": "warning", "message": "Warning in batch", "service": "test-suite", "test_id": "6.2"}]' http://localhost:5050/webhook
 | 
				
			||||||
 | 
					verify_result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Clean up the server
 | 
				
			||||||
 | 
					echo -e "\n${BLUE}Tests completed. Shutting down server...${NC}"
 | 
				
			||||||
 | 
					kill $SERVER_PID || true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo -e "\n${GREEN}All tests passed successfully!${NC}" 
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ARCHITECTURES=("linux/amd64" "linux/arm64" "darwin/amd64" "darwin/arm64" "windows/amd64")
 | 
				
			||||||
 | 
					PROJECT_NAME=$(basename "$(pwd)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					prepare_build() {
 | 
				
			||||||
 | 
					    mkdir -p dist build_logs
 | 
				
			||||||
 | 
					    if [ ! -f go.mod ]; then
 | 
				
			||||||
 | 
					        go mod init "$PROJECT_NAME"
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					    go mod tidy
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					build_binary() {
 | 
				
			||||||
 | 
					    local os=$1
 | 
				
			||||||
 | 
					    local arch=$2
 | 
				
			||||||
 | 
					    local output="dist/${PROJECT_NAME}_${os}_${arch}"
 | 
				
			||||||
 | 
					    env GOOS=$os GOARCH=$arch go build -o $output . &> "build_logs/${os}_${arch}.log"
 | 
				
			||||||
 | 
					    if [ $? -ne 0 ]; then
 | 
				
			||||||
 | 
					        echo "Build failed for $os/$arch" >> "build_logs/error.log"
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					        echo "Build succeeded for $os/$arch"
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					    if [ "$os" == "linux" ]; then
 | 
				
			||||||
 | 
					        env GOOS=$os GOARCH=$arch CGO_ENABLED=0 go build -a -ldflags '-extldflags "-static"' -o "${output}_static" . &> "build_logs/${os}_${arch}_static.log"
 | 
				
			||||||
 | 
					        if [ $? -ne 0 ]; then
 | 
				
			||||||
 | 
					            echo "Static build failed for $os/$arch" >> "build_logs/error.log"
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            echo "Static build succeeded for $os/$arch"
 | 
				
			||||||
 | 
					        fi
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					main() {
 | 
				
			||||||
 | 
					    prepare_build
 | 
				
			||||||
 | 
					    for arch in "${ARCHITECTURES[@]}"; do
 | 
				
			||||||
 | 
					        IFS="/" read -r os arch <<< "$arch"
 | 
				
			||||||
 | 
					        build_binary $os $arch
 | 
				
			||||||
 | 
					    done
 | 
				
			||||||
 | 
					    echo "Build process completed."
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					main
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,7 @@
 | 
				
			||||||
 | 
					Build failed for linux/amd64
 | 
				
			||||||
 | 
					Static build failed for linux/amd64
 | 
				
			||||||
 | 
					Build failed for linux/arm64
 | 
				
			||||||
 | 
					Static build failed for linux/arm64
 | 
				
			||||||
 | 
					Build failed for darwin/amd64
 | 
				
			||||||
 | 
					Build failed for darwin/arm64
 | 
				
			||||||
 | 
					Build failed for windows/amd64
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										53
									
								
								go-glitch.go
								
								
								
								
							
							
						
						
									
										53
									
								
								go-glitch.go
								
								
								
								
							| 
						 | 
					@ -1,53 +0,0 @@
 | 
				
			||||||
package main
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
    "fmt"
 | 
					 | 
				
			||||||
    "os"
 | 
					 | 
				
			||||||
    "time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    "github.com/getsentry/sentry-go"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func main() {
 | 
					 | 
				
			||||||
    // Check for the right number of command line arguments
 | 
					 | 
				
			||||||
    if len(os.Args) != 2 {
 | 
					 | 
				
			||||||
        fmt.Println("Usage: ./go-glitch \"Your log message here\"")
 | 
					 | 
				
			||||||
        os.Exit(1)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    logMessage := os.Args[1]
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    dsn := os.Getenv("SENTRY_DSN")
 | 
					 | 
				
			||||||
    if dsn == "" {
 | 
					 | 
				
			||||||
        fmt.Println("Error: SENTRY_DSN environment variable is not set.")
 | 
					 | 
				
			||||||
        os.Exit(1)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Initialize Sentry with the DSN
 | 
					 | 
				
			||||||
    err := sentry.Init(sentry.ClientOptions{
 | 
					 | 
				
			||||||
        Dsn: dsn,
 | 
					 | 
				
			||||||
    })
 | 
					 | 
				
			||||||
    if err != nil {
 | 
					 | 
				
			||||||
        fmt.Printf("Error initializing Sentry: %s\n", err)
 | 
					 | 
				
			||||||
        os.Exit(1)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Defer a function to flush Sentry's buffer
 | 
					 | 
				
			||||||
    defer func() {
 | 
					 | 
				
			||||||
        success := sentry.Flush(5 * time.Second)
 | 
					 | 
				
			||||||
        if !success {
 | 
					 | 
				
			||||||
            fmt.Println("Failed to flush Sentry buffer within the expected time.")
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            fmt.Println("Sentry buffer flushed successfully.")
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }()
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Capture the message and output the event ID
 | 
					 | 
				
			||||||
    eventID := sentry.CaptureMessage(logMessage)
 | 
					 | 
				
			||||||
    if eventID != nil {
 | 
					 | 
				
			||||||
        fmt.Printf("Sent message to Sentry with event ID: %s\n", *eventID)
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
        fmt.Println("Failed to send message to Sentry.")
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										5
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										5
									
								
								go.mod
								
								
								
								
							| 
						 | 
					@ -1,9 +1,10 @@
 | 
				
			||||||
module go-glitch
 | 
					module go-sink
 | 
				
			||||||
 | 
					
 | 
				
			||||||
go 1.21.1
 | 
					go 1.21.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require github.com/getsentry/sentry-go v0.27.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/getsentry/sentry-go v0.27.0 // indirect
 | 
					 | 
				
			||||||
	golang.org/x/sys v0.6.0 // indirect
 | 
						golang.org/x/sys v0.6.0 // indirect
 | 
				
			||||||
	golang.org/x/text v0.8.0 // indirect
 | 
						golang.org/x/text v0.8.0 // indirect
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										16
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										16
									
								
								go.sum
								
								
								
								
							| 
						 | 
					@ -1,6 +1,22 @@
 | 
				
			||||||
 | 
					github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
				
			||||||
 | 
					github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
 | 
					github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
 | 
				
			||||||
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
 | 
					github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
 | 
				
			||||||
 | 
					github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
 | 
				
			||||||
 | 
					github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
 | 
				
			||||||
 | 
					github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 | 
				
			||||||
 | 
					github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 | 
				
			||||||
 | 
					github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
 | 
				
			||||||
 | 
					github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
 | 
				
			||||||
 | 
					github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
				
			||||||
 | 
					github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
				
			||||||
 | 
					github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 | 
				
			||||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
 | 
					golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
 | 
				
			||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
 | 
					golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
 | 
				
			||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 | 
					golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 | 
				
			||||||
 | 
					gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 | 
				
			||||||
 | 
					gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,36 @@
 | 
				
			||||||
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INSTALL_DIR="/usr/local/bin"
 | 
				
			||||||
 | 
					BINARY_NAME="go-sink"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Default to v0.0.1 if no version is specified
 | 
				
			||||||
 | 
					VERSION=${1:-"v0.0.1"}
 | 
				
			||||||
 | 
					BASE_URL="https://git.nixc.us/Nixius/go-sink/raw/tag/${VERSION}/dist"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Supported architectures
 | 
				
			||||||
 | 
					ARCHITECTURES=("linux/amd64" "linux/arm64" "linux/arm/v7" "darwin/amd64" "darwin/arm64")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
 | 
				
			||||||
 | 
					ARCH="$(uname -m)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					case $ARCH in
 | 
				
			||||||
 | 
					    x86_64) ARCH="amd64" ;;
 | 
				
			||||||
 | 
					    arm64 | aarch64) ARCH="arm64" ;;
 | 
				
			||||||
 | 
					    arm*) ARCH="arm/v7" ;;
 | 
				
			||||||
 | 
					    *) echo "Unsupported architecture: $ARCH"; exit 1 ;;
 | 
				
			||||||
 | 
					esac
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BINARY_URL="${BASE_URL}/${BINARY_NAME}_${OS}_${ARCH}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo "Downloading and installing $BINARY_NAME version ${VERSION} from $BINARY_URL..."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Check if we have write permission to the install directory
 | 
				
			||||||
 | 
					if [ -w "${INSTALL_DIR}" ]; then
 | 
				
			||||||
 | 
					    curl -sSL "$BINARY_URL" -o "${INSTALL_DIR}/${BINARY_NAME}"
 | 
				
			||||||
 | 
					    chmod +x "${INSTALL_DIR}/${BINARY_NAME}"
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					    curl -sSL "$BINARY_URL" -o "${INSTALL_DIR}/${BINARY_NAME}"
 | 
				
			||||||
 | 
					    chmod +x "${INSTALL_DIR}/${BINARY_NAME}"
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					echo "Installed $BINARY_NAME version ${VERSION} to $INSTALL_DIR"
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,312 @@
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bufio"
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"flag"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/getsentry/sentry-go"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getDSN() string {
 | 
				
			||||||
 | 
						// Try multiple environment variables for DSN
 | 
				
			||||||
 | 
						dsn := getEnvDSN()
 | 
				
			||||||
 | 
						if dsn == "" {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "Error: No DSN environment variable found.\n")
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "Please set BUGSINK_DSN (or GLITCHTIP_DSN/SENTRY_DSN for compatibility)\n")
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return dsn
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						// Set up flag usage
 | 
				
			||||||
 | 
						flag.Usage = func() {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "go-sink - A tool for sending logs to error tracking systems\n\n")
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "Usage: %s [options] [file]\n\n", os.Args[0])
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "Options:\n")
 | 
				
			||||||
 | 
							flag.PrintDefaults()
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "\nEnvironment Variables:\n")
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "  BUGSINK_DSN    DSN for BugSink (primary)\n")
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "  Also accepts GLITCHTIP_DSN and SENTRY_DSN for compatibility\n")
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "\nExamples:\n")
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "  %s logfile.txt\n", os.Args[0])
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "  cat logfile.txt | %s\n", os.Args[0])
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "  %s server\n", os.Args[0])
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "  %s --dsn \"https://example.com/123\" logfile.txt\n", os.Args[0])
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "\nVersion: %s\n", VersionInfo())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Add version and help flags
 | 
				
			||||||
 | 
						showVersion := flag.Bool("version", false, "Show version information")
 | 
				
			||||||
 | 
						showHelp := flag.Bool("help", false, "Show help information")
 | 
				
			||||||
 | 
						flag.BoolVar(showHelp, "h", false, "Show help information (shorthand)")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Add DSN flag
 | 
				
			||||||
 | 
						dsnFlag := flag.String("dsn", "", "Override DSN for this execution (takes precedence over environment variables)")
 | 
				
			||||||
 | 
						flag.Parse()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if *showHelp {
 | 
				
			||||||
 | 
							flag.Usage()
 | 
				
			||||||
 | 
							os.Exit(0)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if *showVersion {
 | 
				
			||||||
 | 
							fmt.Println(VersionInfo())
 | 
				
			||||||
 | 
							os.Exit(0)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Initialize Sentry
 | 
				
			||||||
 | 
						var dsn string
 | 
				
			||||||
 | 
						if *dsnFlag != "" {
 | 
				
			||||||
 | 
							dsn = *dsnFlag
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							dsn = getDSN()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := sentry.Init(sentry.ClientOptions{
 | 
				
			||||||
 | 
							Dsn: dsn,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatalf("sentry.Init: %s", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(os.Args) > 1 && os.Args[1] == "server" {
 | 
				
			||||||
 | 
							startServer()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						processLogs()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							sentry.Flush(2 * time.Second)
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// getEnvDSN checks multiple environment variables for a DSN
 | 
				
			||||||
 | 
					func getEnvDSN() string {
 | 
				
			||||||
 | 
						// Try different environment variables in order of preference
 | 
				
			||||||
 | 
						envVars := []string{
 | 
				
			||||||
 | 
							"GLITCHTIP_DSN",
 | 
				
			||||||
 | 
							"SENTRY_DSN",
 | 
				
			||||||
 | 
							"BUGSINK_DSN",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, envVar := range envVars {
 | 
				
			||||||
 | 
							if dsn := os.Getenv(envVar); dsn != "" {
 | 
				
			||||||
 | 
								fmt.Printf("Using DSN from %s environment variable\n", envVar)
 | 
				
			||||||
 | 
								return dsn
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ""
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func processLogs() {
 | 
				
			||||||
 | 
						var reader io.Reader
 | 
				
			||||||
 | 
						var filePath string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(os.Args) == 2 {
 | 
				
			||||||
 | 
							filePath = os.Args[1]
 | 
				
			||||||
 | 
							file, err := os.Open(filePath)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								fmt.Fprintf(os.Stderr, "Error opening file: %s\n", err)
 | 
				
			||||||
 | 
								os.Exit(1)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							defer file.Close()
 | 
				
			||||||
 | 
							reader = file
 | 
				
			||||||
 | 
							fmt.Println("Reading from file:", filePath)
 | 
				
			||||||
 | 
						} else if isInputFromPipe() {
 | 
				
			||||||
 | 
							reader = os.Stdin
 | 
				
			||||||
 | 
							fmt.Println("Reading from stdin")
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "Usage: %s <path to log file> OR pipe input\n", os.Args[0])
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						scanner := bufio.NewScanner(reader)
 | 
				
			||||||
 | 
						var content strings.Builder
 | 
				
			||||||
 | 
						for scanner.Scan() {
 | 
				
			||||||
 | 
							line := scanner.Text()
 | 
				
			||||||
 | 
							content.WriteString(line)
 | 
				
			||||||
 | 
							content.WriteString("\n")
 | 
				
			||||||
 | 
							fmt.Println("Read line:", line)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := scanner.Err(); err != nil {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "Error reading input: %s\n", err)
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						logMessage := content.String()
 | 
				
			||||||
 | 
						fmt.Println("Final log message:", logMessage)
 | 
				
			||||||
 | 
						sendToSentry(logMessage)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isInputFromPipe() bool {
 | 
				
			||||||
 | 
						fileInfo, err := os.Stdin.Stat()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return fileInfo.Mode()&os.ModeCharDevice == 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func sendToSentry(logMessage string) {
 | 
				
			||||||
 | 
						fmt.Println("Sending log message to Sentry:", logMessage)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Try to parse as JSON first
 | 
				
			||||||
 | 
						var parsedLog map[string]interface{}
 | 
				
			||||||
 | 
						err := json.Unmarshal([]byte(logMessage), &parsedLog)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							// Successfully parsed JSON
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Check for common log fields
 | 
				
			||||||
 | 
							level := "info"
 | 
				
			||||||
 | 
							message := logMessage
 | 
				
			||||||
 | 
							tags := make(map[string]string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if lvl, ok := parsedLog["level"].(string); ok {
 | 
				
			||||||
 | 
								level = lvl
 | 
				
			||||||
 | 
							} else if lvl, ok := parsedLog["log_level"].(string); ok {
 | 
				
			||||||
 | 
								level = lvl
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if msg, ok := parsedLog["message"].(string); ok {
 | 
				
			||||||
 | 
								message = msg
 | 
				
			||||||
 | 
							} else if msg, ok := parsedLog["msg"].(string); ok {
 | 
				
			||||||
 | 
								message = msg
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Add any additional fields as tags
 | 
				
			||||||
 | 
							for k, v := range parsedLog {
 | 
				
			||||||
 | 
								if k != "level" && k != "log_level" && k != "message" && k != "msg" {
 | 
				
			||||||
 | 
									if strVal, ok := v.(string); ok {
 | 
				
			||||||
 | 
										tags[k] = strVal
 | 
				
			||||||
 | 
									} else if jsonVal, err := json.Marshal(v); err == nil {
 | 
				
			||||||
 | 
										tags[k] = string(jsonVal)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Create a more structured event based on the level
 | 
				
			||||||
 | 
							var eventID *sentry.EventID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							sentry.WithScope(func(scope *sentry.Scope) {
 | 
				
			||||||
 | 
								scope.SetLevel(getSentryLevel(level))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Add all the extracted fields as tags
 | 
				
			||||||
 | 
								for k, v := range tags {
 | 
				
			||||||
 | 
									scope.SetTag(k, v)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Capture based on level
 | 
				
			||||||
 | 
								if getSentryLevel(level) >= sentry.LevelError {
 | 
				
			||||||
 | 
									eventID = sentry.CaptureException(fmt.Errorf(message))
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									eventID = sentry.CaptureMessage(message)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if eventID != nil {
 | 
				
			||||||
 | 
								fmt.Printf("Sent message to GlitchTip with event ID: %s\n", *eventID)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								fmt.Fprintf(os.Stderr, "Failed to send message to GlitchTip.\n")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// Not a valid JSON, send as a simple message
 | 
				
			||||||
 | 
							eventID := sentry.CaptureMessage(logMessage)
 | 
				
			||||||
 | 
							if eventID != nil {
 | 
				
			||||||
 | 
								fmt.Printf("Sent message to GlitchTip with event ID: %s\n", *eventID)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								fmt.Fprintf(os.Stderr, "Failed to send message to GlitchTip.\n")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Helper function to convert string log levels to Sentry levels
 | 
				
			||||||
 | 
					func getSentryLevel(level string) sentry.Level {
 | 
				
			||||||
 | 
						switch strings.ToLower(level) {
 | 
				
			||||||
 | 
						case "fatal", "panic":
 | 
				
			||||||
 | 
							return sentry.LevelFatal
 | 
				
			||||||
 | 
						case "error", "err":
 | 
				
			||||||
 | 
							return sentry.LevelError
 | 
				
			||||||
 | 
						case "warning", "warn":
 | 
				
			||||||
 | 
							return sentry.LevelWarning
 | 
				
			||||||
 | 
						case "info":
 | 
				
			||||||
 | 
							return sentry.LevelInfo
 | 
				
			||||||
 | 
						case "debug":
 | 
				
			||||||
 | 
							return sentry.LevelDebug
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return sentry.LevelInfo
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func startServer() {
 | 
				
			||||||
 | 
						http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							bodyBytes, err := io.ReadAll(r.Body)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								http.Error(w, "Error reading request body", http.StatusInternalServerError)
 | 
				
			||||||
 | 
								fmt.Fprintf(os.Stderr, "Error reading request body: %s\n", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							fmt.Printf("Received request body: %s\n", bodyBytes) // Log the full request body
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var logMessage interface{}
 | 
				
			||||||
 | 
							err = json.Unmarshal(bodyBytes, &logMessage)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								http.Error(w, "Invalid JSON", http.StatusBadRequest)
 | 
				
			||||||
 | 
								fmt.Fprintf(os.Stderr, "Invalid JSON: %s\nReceived: %s\n", err, string(bodyBytes))
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch v := logMessage.(type) {
 | 
				
			||||||
 | 
							case map[string]interface{}:
 | 
				
			||||||
 | 
								logBytes, err := json.Marshal(v)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									http.Error(w, "Error processing log message", http.StatusInternalServerError)
 | 
				
			||||||
 | 
									fmt.Fprintf(os.Stderr, "Error processing log message: %s\n", err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								fmt.Printf("Processed log: %s\n", logBytes)
 | 
				
			||||||
 | 
								sendToSentry(string(logBytes))
 | 
				
			||||||
 | 
							case []interface{}:
 | 
				
			||||||
 | 
								for _, item := range v {
 | 
				
			||||||
 | 
									if logItem, ok := item.(map[string]interface{}); ok {
 | 
				
			||||||
 | 
										logBytes, err := json.Marshal(logItem)
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											http.Error(w, "Error processing log message", http.StatusInternalServerError)
 | 
				
			||||||
 | 
											fmt.Fprintf(os.Stderr, "Error processing log message: %s\n", err)
 | 
				
			||||||
 | 
											return
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										fmt.Printf("Processed log item: %s\n", logBytes)
 | 
				
			||||||
 | 
										sendToSentry(string(logBytes))
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										http.Error(w, "Invalid JSON item in array", http.StatusBadRequest)
 | 
				
			||||||
 | 
										fmt.Fprintf(os.Stderr, "Invalid JSON item in array: %s\n", item)
 | 
				
			||||||
 | 
										return
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								http.Error(w, "Invalid JSON format", http.StatusBadRequest)
 | 
				
			||||||
 | 
								fmt.Fprintf(os.Stderr, "Invalid JSON format: %s\n", v)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							w.WriteHeader(http.StatusNoContent)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println("Starting server on 0.0.0.0:5050")
 | 
				
			||||||
 | 
						err := http.ListenAndServe("0.0.0.0:5050", nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "Error starting server: %s\n", err)
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,4 @@
 | 
				
			||||||
 | 
					#!/bin/bash
 | 
				
			||||||
 | 
					curl -X POST -H "Content-Type: application/json" -d '{"log_level": "error", "message": "This is an error log message via TEST.SH"}' http://localhost:5050/webhook
 | 
				
			||||||
 | 
					echo test | ./dist/go-sink_darwin_arm64
 | 
				
			||||||
 | 
					./dist/go-sink_darwin_arm64 log.log
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						VersionMajor = 0
 | 
				
			||||||
 | 
						VersionMinor = 0
 | 
				
			||||||
 | 
						VersionPatch = 1
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Version returns the current version as a string
 | 
				
			||||||
 | 
					func Version() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// VersionInfo returns detailed version information
 | 
				
			||||||
 | 
					func VersionInfo() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("go-sink version %s", Version())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue