├── LICENSE ├── README.md └── index.js /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cf-dnalog-worker 2 | simple cloudflare worker recipe to send logs into [logdna](https://logdna.com/) 3 | 4 | ## how to use 5 | copy index.js content into your worker 6 | change myApp and myHostName into whatever you want, read the logdna [Ingest API](https://docs.logdna.com/v1.0/reference#api) 7 | 8 | * maxRequestsPerBatch - how many requests to maximum batch per sending, by default it send all the batched requests once per 10 seconds 9 | 10 | ## about compiledPass 11 | 12 | you should precompile your logdna ingestion key and store it in the compilePass parameters, you can simply type in console `btoa(username+':'+password)` where username is your ingestion key and password keep empty, and put the results into the parameter to save some cpu time(probably) 13 | 14 | 15 | I left all the console.log command to better help you debug 16 | 17 | ### logged parameters: 18 | 19 | + user agent 20 | + referer 21 | + ip 22 | + countryCode 23 | + url 24 | + colo 25 | + workerInception 26 | + workerId 27 | + method 28 | + x_forwarded_for 29 | + asn 30 | + status 31 | + originTime 32 | + CF-Cache-Status 33 | + CF-Ray 34 | + tlsCipher 35 | + tlsVersion 36 | + clientTrustScore -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | let requests = []; 2 | let requestStartTime, requestEndTime; 3 | let batchIsRunning = false; 4 | const maxRequestsPerBatch = 150; 5 | 6 | addEventListener('fetch', event => { 7 | event.passThroughOnException(); 8 | event.respondWith(logRequests(event)); 9 | }) 10 | 11 | async function logRequests(event) { 12 | if (!batchIsRunning) { 13 | event.waitUntil(handleBatch(event)); 14 | } 15 | if (requests.length >= maxRequestsPerBatch) { 16 | event.waitUntil(postRequests()) 17 | } 18 | requestStartTime = Date.now(); 19 | const response = await fetch(event.request) 20 | requestEndTime = Date.now(); 21 | await requests.push(getRequestData(event.request, response)); 22 | return response 23 | } 24 | 25 | async function handleBatch(event) { 26 | batchIsRunning = true; 27 | await sleep(10000) 28 | try { 29 | if (requests.length) event.waitUntil(postRequests()) 30 | } catch (e) { 31 | 32 | } 33 | requests = []; 34 | batchIsRunning = false; 35 | } 36 | 37 | function sleep(ms) { 38 | return new Promise(resolve => { 39 | setTimeout(resolve, ms) 40 | }) 41 | } 42 | 43 | function getRequestData(request, re) { 44 | let data = { 45 | 'app': 'myApp', 46 | 'timestamp': Date.now(), 47 | 'meta': { 48 | 'ua': request.headers.get('user-agent'), 49 | 'referer': request.headers.get('Referer') || 'empty', 50 | 'ip': request.headers.get('CF-Connecting-IP'), 51 | 'countryCode': (request.cf || {}).country, 52 | 'colo': (request.cf || {}).colo, 53 | 'url': request.url, 54 | 'method': request.method, 55 | 'x_forwarded_for': request.headers.get('x_forwarded_for') || "0.0.0.0", 56 | 'asn': (request.cf || {}).asn, 57 | 'cfRay': request.headers.get('cf-ray'), 58 | 'tlsCipher': (request.cf || {}).tlsCipher, 59 | 'tlsVersion': (request.cf || {}).tlsVersion, 60 | 'clientTrustScore': (request.cf || {}).clientTrustScore, 61 | 'status': (re || {}).status, 62 | 'originTime': (requestEndTime - requestStartTime), 63 | 'cfCache': (re) ? (re.headers.get('CF-Cache-Status') || 'miss') : 'MISS', 64 | } 65 | }; 66 | data.line = data.meta.status + " " + data.meta.countryCode + " " + data.meta.cfCache + " " + data.meta.originTime + 'ms' + " " + data.meta.ip + " " + data.meta.url; 67 | return data; 68 | } 69 | 70 | async function postRequests() { 71 | //console.log('posting',data); 72 | let data = JSON.stringify({'lines': requests}); 73 | const username = 'My logdna Ingestion key'; 74 | const password = ''; 75 | const compiledPass = ''; 76 | const hostname = 'example.com'; 77 | let myHeaders = new Headers(); 78 | myHeaders.append('Content-Type', 'application/json; charset=UTF-8'); 79 | myHeaders.append('Authorization', 'Basic ' + (compiledPass || btoa(username + ':' + password))); 80 | try { 81 | return fetch('https://logs.logdna.com/logs/ingest?tag=worker&hostname=' + hostname, { 82 | method: 'POST', 83 | headers: myHeaders, 84 | body: data 85 | }).then(function (r) { 86 | requests = []; 87 | }); 88 | } catch (err) { 89 | //console.log(err.stack || err); 90 | } 91 | } 92 | --------------------------------------------------------------------------------