Skip to content

Instantly share code, notes, and snippets.

@easierbycode
Last active December 8, 2025 17:35
Show Gist options
  • Select an option

  • Save easierbycode/a2dfe8a6f3ba4876cc5448388e50dab4 to your computer and use it in GitHub Desktop.

Select an option

Save easierbycode/a2dfe8a6f3ba4876cc5448388e50dab4 to your computer and use it in GitHub Desktop.
#!/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"
// 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();
/**
* 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 };
// 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