Last active
December 8, 2025 17:35
-
-
Save easierbycode/a2dfe8a6f3ba4876cc5448388e50dab4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| PORT=12201 | |
| INTF=eth0 # change if your external interface is different | |
| SOURCE_CIDR="0.0.0.0/0" # no restriction — allows any source (for testing) | |
| echo "1) Verify Graylog is listening on port $PORT:" | |
| ss -tulpen | grep ":$PORT" | |
| echo | |
| echo "2) Check current iptables INPUT rules for port $PORT:" | |
| sudo iptables -L INPUT -n --line-numbers | grep "$PORT" | |
| echo | |
| echo "3) (Optional) Add a rule to allow incoming TCP on $PORT temporarily:" | |
| sudo iptables -I INPUT 1 -p tcp --dport $PORT -s $SOURCE_CIDR -j ACCEPT | |
| echo "Added iptables rule to ACCEPT TCP port $PORT from anywhere." | |
| echo | |
| echo "4) (Optional) If using firewalld:" | |
| if command -v firewall-cmd >/dev/null 2>&1; then | |
| echo "Using firewalld — adding port $PORT/tcp" | |
| sudo firewall-cmd --permanent --add-port=${PORT}/tcp | |
| sudo firewall-cmd --reload | |
| echo "firewalld rule added." | |
| fi | |
| echo | |
| echo "5) (Optional) Start tcpdump on interface $INTF to observe incoming traffic on port $PORT (requires root):" | |
| echo " sudo tcpdump -i $INTF port $PORT -vv" | |
| echo "Then from your client run curl / Node.js to send test log — see if packets hit server." | |
| echo | |
| echo "6) Test from server itself with curl (loopback):" | |
| curl -v -H "Content-Type: application/json" \ | |
| -d '{"version":"1.1","host":"server-self-test","short_message":"test from server"}' \ | |
| http://127.0.0.1:$PORT/gelf && echo "Self-test HTTP POST succeeded" || echo "Self-test HTTP POST failed" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // send-graylog-test.js | |
| const axios = require('axios'); | |
| async function sendTestLog() { | |
| const url = 'http://graylog-server.thirsty.store:12201/gelf'; | |
| const gelfMessage = { | |
| version: '1.1', | |
| host: 'Lifepreneur', | |
| short_message: 'Test log from Node.js on Windows', | |
| full_message: 'If you see this, external GELF HTTP is working.', | |
| level: 6, | |
| _app: 'Lifepreneur', | |
| }; | |
| // const gelfMessage = { | |
| // event: 'like', | |
| // data: { | |
| // likeCount: 3, | |
| // totalLikeCount: 520, | |
| // userId: '7512006564394845230', | |
| // secUid: 'MS4wLjABAAAAhsjTE89g4CU3l-vb07sAGtULcfRdg6XRz4eZu_Dh0kBA_vPrbXjMsz0h3cgDgC7D', | |
| // uniqueId: 'christineellis51', | |
| // nickname: 'Christine Ellis', | |
| // profilePictureUrl: 'https://p16-pu-sign-useast8.tiktokcdn-us.com/tos-useast5-avt-0068-tx/de9bb16f0517432e283cdf11011a93fa~tplv-tiktokx-cropcenter:100:100.webp?dr=9640&refresh_token=b6a1642b&x-expires=1750032000&x-signature=0wJ95Dh9aESK0GIt83I2RDT72w8%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=fdd36af4&idc=useast8', | |
| // followRole: 1, | |
| // userBadges: [ | |
| // { | |
| // type: 'image', | |
| // badgeSceneType: 6, | |
| // displayType: 1, | |
| // url: 'https://p19-webcast.tiktokcdn.com/webcast-sg/new_top_gifter_version_2.png~tplv-obj.image' | |
| // } | |
| // ], | |
| // userSceneTypes: [ | |
| // 6, | |
| // 6 | |
| // ], | |
| // userDetails: { | |
| // createTime: '0', | |
| // bioDescription: '', | |
| // profilePictureUrls: [ | |
| // 'https://p16-pu-sign-useast8.tiktokcdn-us.com/tos-useast5-avt-0068-tx/de9bb16f0517432e283cdf11011a93fa~tplv-tiktokx-cropcenter:100:100.webp?dr=9640&refresh_token=b6a1642b&x-expires=1750032000&x-signature=0wJ95Dh9aESK0GIt83I2RDT72w8%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=fdd36af4&idc=useast8', | |
| // 'https://p19-pu-sign-useast8.tiktokcdn-us.com/tos-useast5-avt-0068-tx/de9bb16f0517432e283cdf11011a93fa~tplv-tiktokx-cropcenter:100:100.webp?dr=9640&refresh_token=4f7a589c&x-expires=1750032000&x-signature=4tN6mIyxWWdg1LJeQK%2BsnvCX488%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=fdd36af4&idc=useast8', | |
| // 'https://p16-pu-sign-useast8.tiktokcdn-us.com/tos-useast5-avt-0068-tx/de9bb16f0517432e283cdf11011a93fa~tplv-tiktokx-cropcenter:100:100.jpeg?dr=9640&refresh_token=0ea81d5e&x-expires=1750032000&x-signature=C%2Bk4x%2FDlq0FQ7YsZrVqD%2FMkOlbg%3D&t=4d5b0474&ps=13740610&shp=a5d48078&shcp=fdd36af4&idc=useast8' | |
| // ] | |
| // }, | |
| // followInfo: { | |
| // followingCount: 1, | |
| // followerCount: 0, | |
| // followStatus: 1, | |
| // pushStatus: 0 | |
| // }, | |
| // isModerator: false, | |
| // isNewGifter: false, | |
| // isSubscriber: false, | |
| // topGifterRank: 1, | |
| // gifterLevel: 0, | |
| // teamMemberLevel: 0, | |
| // msgId: '7515588820285277214', | |
| // createTime: '1749859382884', | |
| // displayType: 'pm_mt_msg_viewer', | |
| // label: '{0:user} liked the LIVE', | |
| // tikfinityUserId: 1888822, | |
| // tikfinityUsername: 'wizardofdealz' | |
| // } | |
| // }; | |
| try { | |
| const res = await axios.post(url, gelfMessage, { | |
| headers: { 'Content-Type': 'application/json' }, | |
| timeout: 5000, | |
| }); | |
| console.log('Sent log, HTTP status:', res.status); | |
| } catch (err) { | |
| console.error('Error sending log:', err.message); | |
| } | |
| } | |
| sendTestLog(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Module: graylogSender.js | |
| * Description: Sends structured JSON data (product KPIs) to Graylog via GELF HTTP. | |
| * Requires: axios (npm install axios) | |
| */ | |
| const axios = require('axios'); | |
| const os = require('os'); | |
| // Configurable Graylog endpoint (override via environment variables if needed) | |
| const GRAYLOG_HOST = process.env.GRAYLOG_HOST || 'graylog-server.thirsty.store'; | |
| const GRAYLOG_PORT = process.env.GRAYLOG_PORT || '12201'; | |
| const GRAYLOG_PATH = process.env.GRAYLOG_PATH || '/gelf'; // default GELF HTTP path:contentReference[oaicite:5]{index=5} | |
| const GRAYLOG_PROTOCOL = process.env.GRAYLOG_PROTOCOL || 'http'; // use 'https' if TLS is enabled on Graylog input | |
| // Construct the full Graylog URL for posting GELF messages | |
| const GRAYLOG_URL = `${GRAYLOG_PROTOCOL}://${GRAYLOG_HOST}:${GRAYLOG_PORT}${GRAYLOG_PATH}`; | |
| // Define required fields for the KPI JSON | |
| const REQUIRED_FIELDS = ['product', 'daily_sales', 'clicks', 'CTR', 'CR']; | |
| /** | |
| * Sends a JSON object with product KPI data to Graylog using GELF over HTTP. | |
| * @param {Object} data - The KPI data (must include product, daily_sales, clicks, CTR, CR). | |
| * @returns {Promise<void>} Promise that resolves when send is complete (or rejects on fatal error). | |
| */ | |
| const sendToGraylog = async (data) => { | |
| // Validate input contains all required fields | |
| const missing = REQUIRED_FIELDS.filter(field => data[field] === undefined); | |
| if (missing.length > 0) { | |
| console.error(`Graylog send failed: missing fields [${missing.join(', ')}] in data payload`); | |
| return; // Exit without sending, since data is incomplete | |
| } | |
| // Prepare GELF message payload. | |
| // Graylog requires "version", "host", and "short_message" fields in every GELF message:contentReference[oaicite:6]{index=6}:contentReference[oaicite:7]{index=7}. | |
| // Additional custom fields are prefixed with an underscore:contentReference[oaicite:8]{index=8}. | |
| const gelfMessage = { | |
| // host: process.env.GELF_HOSTNAME // optional override for source host | |
| // || os.hostname(), // default to local hostname as source | |
| host: 'Lifepreneur', // static hostname for source identification | |
| version: "1.1", // GELF version (mandatory) | |
| short_message: `Product KPI: ${data.product}`, // brief description for Graylog's message field | |
| timestamp: Date.now() / 1000.0, // current time in seconds (optional; Graylog uses current time if not provided) | |
| level: 6, // syslog level 6 = Informational (optional) | |
| _product: data.product, // Custom fields (prefix with '_' for Graylog) | |
| _daily_sales: data.daily_sales, | |
| _clicks: data.clicks, | |
| _CTR: data.CTR, | |
| _CR: data.CR | |
| }; | |
| try { | |
| // Send the GELF message to Graylog via HTTP POST | |
| const response = await axios.post(GRAYLOG_URL, gelfMessage, { | |
| headers: { 'Content-Type': 'application/json' } | |
| }); | |
| if (response.status === 202 || response.status === 201 || response.status === 200) { | |
| // Graylog typically returns HTTP 202 Accepted for GELF HTTP inputs:contentReference[oaicite:9]{index=9}. | |
| // Logging success (optional): | |
| console.log(`Graylog: Successfully sent KPI data for "${data.product}"`); | |
| } else { | |
| console.error(`Graylog: Unexpected response ${response.status} - ${response.statusText}`); | |
| } | |
| } catch (err) { | |
| // Handle errors (e.g., network issues, connection refused, etc.) | |
| console.error(`Graylog: Failed to send data - ${err.message}`); | |
| } | |
| }; | |
| // Export the function for use in other modules | |
| module.exports = { sendToGraylog }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Import the module (adjust the path as needed) | |
| const { sendToGraylog } = require('./graylogSender'); | |
| // Sample product KPI data | |
| const sampleKPI = { | |
| product: "Widget Pro", | |
| daily_sales: 150, | |
| clicks: 3000, | |
| CTR: 0.05, // 5% click-through rate | |
| CR: 0.02 // 2% conversion rate | |
| }; | |
| // Optionally set environment variables for Graylog configuration (here using defaults): | |
| // process.env.GRAYLOG_HOST = 'graylog-server.thirsty.store'; | |
| // process.env.GRAYLOG_PORT = '12201'; | |
| // process.env.GRAYLOG_PATH = '/gelf'; // default path for HTTP GELF | |
| // process.env.GRAYLOG_PROTOCOL = 'http'; // use 'https' if Graylog input is TLS-enabled | |
| // Send the sample data to Graylog | |
| sendToGraylog(sampleKPI) | |
| .then(() => { | |
| console.log("KPI log attempted. (Check Graylog for ingestion.)"); | |
| }) | |
| .catch(err => { | |
| console.error("Error in sending KPI log:", err); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment