@logmint/sdk 1.1.1
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.
- package/README.md +87 -0
- package/cloud/addLogs.js +108 -0
- package/cloud/addMetrics.js +39 -0
- package/cloud/retryQueue.js +43 -0
- package/cloud/sendToCloud.js +48 -0
- package/core/config.js +62 -0
- package/core/validator.js +7 -0
- package/index.js +136 -0
- package/local/initLocal.js +12 -0
- package/local/migration.sql +12 -0
- package/local/writeLocal.js +30 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Hey there!
|
|
2
|
+
## Welcome to LogMint SDK
|
|
3
|
+
|
|
4
|
+
This SDK will help you log events directly on our cloud or in your local database just with the help of a few lines of code.
|
|
5
|
+
|
|
6
|
+
### Local Setup
|
|
7
|
+
- Install package using
|
|
8
|
+
`npm install @logmint/audit`
|
|
9
|
+
|
|
10
|
+
- For local setup, just initailize and connect to your database:
|
|
11
|
+
```js
|
|
12
|
+
const { init, log } = require("@logmint/audit");
|
|
13
|
+
const { Pool } = require("pg");
|
|
14
|
+
|
|
15
|
+
await init({
|
|
16
|
+
db: {
|
|
17
|
+
client: new Pool({
|
|
18
|
+
host: "localhost",
|
|
19
|
+
user: "postgres",
|
|
20
|
+
password: "admin",
|
|
21
|
+
port: 5432,
|
|
22
|
+
database: "todo",
|
|
23
|
+
}),
|
|
24
|
+
},
|
|
25
|
+
mode: "local",
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The SDK will automatically create an **events** table if it does not exist in your database. If you want to manually create it, use the query:
|
|
30
|
+
```sql
|
|
31
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
32
|
+
id SERIAL PRIMARY KEY,
|
|
33
|
+
event_type TEXT NOT NULL,
|
|
34
|
+
actor_id TEXT NOT NULL,
|
|
35
|
+
actor_name TEXT NOT NULL,
|
|
36
|
+
resource_id TEXT NOT NULL,
|
|
37
|
+
resource_type TEXT NOT NULL,
|
|
38
|
+
ip_address TEXT,
|
|
39
|
+
user_agent TEXT,
|
|
40
|
+
metadata JSONB,
|
|
41
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
42
|
+
);
|
|
43
|
+
```
|
|
44
|
+
### Log events
|
|
45
|
+
```js
|
|
46
|
+
await log({
|
|
47
|
+
event_type: "user.paid.first.order",
|
|
48
|
+
actor_name: "shreya",
|
|
49
|
+
actor_id: "1",
|
|
50
|
+
resource_id: "#1",
|
|
51
|
+
resource_type: "mobile app",
|
|
52
|
+
metadata: { old_column: "old", new_column: "new" },
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
### Cloud Setup
|
|
56
|
+
- Install package using
|
|
57
|
+
`npm install @logmint/audit`
|
|
58
|
+
```js
|
|
59
|
+
const { init, log, flush } = require("@logmint/audit");
|
|
60
|
+
|
|
61
|
+
await init({
|
|
62
|
+
mode: "cloud",
|
|
63
|
+
apiKey: "YOUR_API_KEY",
|
|
64
|
+
secretKey: "YOUR_SECRET_KEY",
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Log events
|
|
69
|
+
In place of URL, use `https://api.getlogmint.com`
|
|
70
|
+
```js
|
|
71
|
+
await log({
|
|
72
|
+
event_type: "user.paid.first.order",
|
|
73
|
+
actor_name: "shreya",
|
|
74
|
+
actor_id: "1",
|
|
75
|
+
resource_id: "#1",
|
|
76
|
+
resource_type: "mobile app",
|
|
77
|
+
metadata: { old_column: "old", new_column: "new" },
|
|
78
|
+
}, `<URL>`);
|
|
79
|
+
```
|
|
80
|
+
If events fail to get logged, it will automatically get pushed to redis. You'll have to flush it out.(Recommended once a week).
|
|
81
|
+
In place of URL, use `https://api.getlogmint.com`
|
|
82
|
+
```js
|
|
83
|
+
const { flush } = require("@logmint/audit");
|
|
84
|
+
|
|
85
|
+
await flush(`<URL>`);
|
|
86
|
+
```
|
|
87
|
+
#### Contact us: support@getlogmint.com
|
package/cloud/addLogs.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
const axios = require("axios");
|
|
2
|
+
const { getConfig } = require("../core/config");
|
|
3
|
+
let originalConsole = {};
|
|
4
|
+
let buffer = { logs: [] };
|
|
5
|
+
|
|
6
|
+
setInterval(() => {
|
|
7
|
+
if (buffer.logs.length > 0) {
|
|
8
|
+
const batch = [...buffer.logs];
|
|
9
|
+
buffer.logs = [];
|
|
10
|
+
addLogs({ logs: batch });
|
|
11
|
+
}
|
|
12
|
+
}, 1000 * 60 * 10); // every 10 minutes
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
const addLogs = async (logs) => {
|
|
16
|
+
|
|
17
|
+
const config = getConfig();
|
|
18
|
+
const url = config.url;
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// verify api key and secret key
|
|
23
|
+
const result = await axios.post(
|
|
24
|
+
`${url}/api/sdk/verify`,
|
|
25
|
+
{
|
|
26
|
+
secret_key: config.secretKey,
|
|
27
|
+
api_key: config.apiKey,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
headers: {
|
|
31
|
+
"Content-Type": "application/json",
|
|
32
|
+
},
|
|
33
|
+
validateStatus: () => true,
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
if (result.status != 200) {
|
|
38
|
+
console.log("Could not add log: ", result.data.msg);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
await axios.post(`${url}/api/logs/bulk`, logs, {
|
|
42
|
+
headers: {
|
|
43
|
+
"x-api-key": `${config.apiKey}`,
|
|
44
|
+
authorization: `Bearer ${config.secretKey}`,
|
|
45
|
+
"Content-Type": "application/json",
|
|
46
|
+
},
|
|
47
|
+
validateStatus: () => true,
|
|
48
|
+
});
|
|
49
|
+
console.log("logs added successfully! -", logs);
|
|
50
|
+
} catch (err) {
|
|
51
|
+
console.log(err);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function getStackInfo() {
|
|
56
|
+
const obj = {};
|
|
57
|
+
Error.captureStackTrace(obj, getStackInfo);
|
|
58
|
+
const stack = obj.stack.split("\n")[2];
|
|
59
|
+
const match = stack.match(/\((.*):(\d+):(\d+)\)$/);
|
|
60
|
+
if (!match) return {};
|
|
61
|
+
return { file: match[1], line: match[2], column: match[3] };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
module.exports.overrideConsoleMethods = () => {
|
|
66
|
+
if (originalConsole.log) return;
|
|
67
|
+
|
|
68
|
+
originalConsole = {
|
|
69
|
+
log: console.log,
|
|
70
|
+
warn: console.warn,
|
|
71
|
+
error: console.error,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const capture = (level, args) => {
|
|
75
|
+
const message = args
|
|
76
|
+
.map((a) => (typeof a === "object" ? JSON.stringify(a) : String(a)))
|
|
77
|
+
.join(" ");
|
|
78
|
+
|
|
79
|
+
const stack = getStackInfo();
|
|
80
|
+
|
|
81
|
+
buffer.logs.push({
|
|
82
|
+
level,
|
|
83
|
+
message,
|
|
84
|
+
metadata: {
|
|
85
|
+
timestamp: new Date().toISOString(),
|
|
86
|
+
...stack,
|
|
87
|
+
hostname: require("os").hostname(),
|
|
88
|
+
pid: process.pid,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
console.log = (...args) => {
|
|
94
|
+
originalConsole.log(...args);
|
|
95
|
+
capture("info", args);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
console.warn = (...args) => {
|
|
99
|
+
originalConsole.warn(...args);
|
|
100
|
+
capture("warn", args);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
console.error = (...args) => {
|
|
104
|
+
originalConsole.error(...args);
|
|
105
|
+
capture("error", args);
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const axios = require("axios");
|
|
2
|
+
const { getConfig } = require("../core/config");
|
|
3
|
+
|
|
4
|
+
module.exports.addMetrics = async (metric) => {
|
|
5
|
+
const config = getConfig();
|
|
6
|
+
const url = config.url;
|
|
7
|
+
try {
|
|
8
|
+
// verify api key and secret key
|
|
9
|
+
const result = await axios.post(
|
|
10
|
+
`${url}/api/sdk/verify`,
|
|
11
|
+
{
|
|
12
|
+
secret_key: config.secretKey,
|
|
13
|
+
api_key: config.apiKey,
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
headers: {
|
|
17
|
+
"Content-Type": "application/json",
|
|
18
|
+
},
|
|
19
|
+
validateStatus: () => true,
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
if (result.status != 200) {
|
|
24
|
+
console.log("Could not add log: ", result.data.msg);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
await axios.post(`${url}/api/metrics`, metric, {
|
|
28
|
+
headers: {
|
|
29
|
+
"x-api-key": `${config.apiKey}`,
|
|
30
|
+
authorization: `Bearer ${config.secretKey}`,
|
|
31
|
+
"Content-Type": "application/json",
|
|
32
|
+
},
|
|
33
|
+
validateStatus: () => true,
|
|
34
|
+
});
|
|
35
|
+
console.log("metric added successfully! -", metric);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
console.log(err);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// retryQueue.js
|
|
2
|
+
const axios = require("axios");
|
|
3
|
+
|
|
4
|
+
async function add(log, config) {
|
|
5
|
+
try {
|
|
6
|
+
const url = config.url;
|
|
7
|
+
await axios.post(
|
|
8
|
+
`${url}/api/redis/queue`,
|
|
9
|
+
{ log, api_key: config.apiKey },
|
|
10
|
+
{
|
|
11
|
+
headers: {
|
|
12
|
+
"x-api-key": `${config.apiKey}`,
|
|
13
|
+
authorization: `Bearer ${config.secretKey}`,
|
|
14
|
+
"Content-Type": "application/json",
|
|
15
|
+
},
|
|
16
|
+
validateStatus: () => true,
|
|
17
|
+
}
|
|
18
|
+
);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.log("Failed to add log to retry queue:", error);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function flush(config) {
|
|
25
|
+
try {
|
|
26
|
+
const url = config.url;
|
|
27
|
+
const result = await axios.post(
|
|
28
|
+
`${url}/api/redis/flush`,
|
|
29
|
+
{ api_key: config.apiKey, secret_key: config.secretKey },
|
|
30
|
+
{
|
|
31
|
+
headers: {
|
|
32
|
+
"Content-Type": "application/json",
|
|
33
|
+
},
|
|
34
|
+
validateStatus: () => true,
|
|
35
|
+
}
|
|
36
|
+
);
|
|
37
|
+
return result;
|
|
38
|
+
} catch (error) {
|
|
39
|
+
return { error: "Failed to flush queue:" + error};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = { add, flush };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const axios = require("axios");
|
|
2
|
+
const { getConfig } = require("../core/config");
|
|
3
|
+
const queue = require("./retryQueue");
|
|
4
|
+
|
|
5
|
+
module.exports.sendToCloud = async (log, isFlush) => {
|
|
6
|
+
const config = getConfig();
|
|
7
|
+
const url = config.url;
|
|
8
|
+
try {
|
|
9
|
+
// verify api key and secret key
|
|
10
|
+
const result = await axios.post(
|
|
11
|
+
`${url}/api/sdk/verify`,
|
|
12
|
+
{
|
|
13
|
+
secret_key: config.secretKey,
|
|
14
|
+
api_key: config.apiKey,
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
headers: {
|
|
18
|
+
"Content-Type": "application/json",
|
|
19
|
+
},
|
|
20
|
+
validateStatus: () => true,
|
|
21
|
+
}
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
if(result.status != 200) {
|
|
25
|
+
console.log("Could not add log: ", result.data.msg);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
await axios.post(`${url}/api/events`, log, {
|
|
29
|
+
headers: {
|
|
30
|
+
"x-api-key": `${config.apiKey}`,
|
|
31
|
+
authorization: `Bearer ${config.secretKey}`,
|
|
32
|
+
"Content-Type": "application/json",
|
|
33
|
+
},
|
|
34
|
+
validateStatus: () => true,
|
|
35
|
+
});
|
|
36
|
+
console.log("log added successfully! -", log);
|
|
37
|
+
|
|
38
|
+
} catch (err) {
|
|
39
|
+
console.log(err);
|
|
40
|
+
|
|
41
|
+
if(!isFlush) {
|
|
42
|
+
queue.add(log, config, url); // fallback to retry queue
|
|
43
|
+
console.log("added to retry queue");
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
return new Error("Cloud send failed → queued");
|
|
47
|
+
}
|
|
48
|
+
};
|
package/core/config.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
let config = Object.freeze({
|
|
2
|
+
mode: "cloud",
|
|
3
|
+
apiKey: null,
|
|
4
|
+
db: null,
|
|
5
|
+
secretKey: null,
|
|
6
|
+
url: null
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const VALID_MODES = ["local", "cloud"];
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Set SDK configuration.
|
|
13
|
+
* Only valid keys (`mode`, `db`, `apiKey`) are accepted.
|
|
14
|
+
* @param {Object} opts
|
|
15
|
+
* @param {"local"|"cloud"} [opts.mode]
|
|
16
|
+
* @param {string|Object} [opts.db]
|
|
17
|
+
* @param {string} [opts.apiKey]
|
|
18
|
+
* @param {string} [opts.secretKey]
|
|
19
|
+
* @param {string} [opts.url]
|
|
20
|
+
*/
|
|
21
|
+
module.exports.setConfig = (opts) => {
|
|
22
|
+
const newConfig = { ...config }; // start with existing config
|
|
23
|
+
|
|
24
|
+
// Only allow known keys
|
|
25
|
+
if ("mode" in opts) {
|
|
26
|
+
if (!VALID_MODES.includes(opts.mode)) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Invalid mode "${opts.mode}". Must be "local" or "cloud".`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
newConfig.mode = opts.mode;
|
|
32
|
+
}
|
|
33
|
+
if (opts.mode == VALID_MODES[0] && !opts.db) {
|
|
34
|
+
throw new Error("database connection string not provided for local mode");
|
|
35
|
+
} else {
|
|
36
|
+
newConfig.db = opts.db;
|
|
37
|
+
}
|
|
38
|
+
if (opts.mode == VALID_MODES[1]) {
|
|
39
|
+
if (!opts.apiKey) {
|
|
40
|
+
throw new Error("API key not provided for cloud mode");
|
|
41
|
+
} else {
|
|
42
|
+
newConfig.apiKey = opts.apiKey;
|
|
43
|
+
if (!opts.secretKey) {
|
|
44
|
+
throw new Error("Secret Key not provided for cloud mode");
|
|
45
|
+
}
|
|
46
|
+
newConfig.secretKey = opts.secretKey;
|
|
47
|
+
if (!opts.url) {
|
|
48
|
+
throw new Error("URL not provided");
|
|
49
|
+
}
|
|
50
|
+
// ensure it starts with http/https
|
|
51
|
+
if (!/^https?:\/\//i.test(opts.url) || typeof opts.url !== "string" || opts.url.trim().length === 0) {
|
|
52
|
+
throw new Error("Invalid URL");
|
|
53
|
+
}
|
|
54
|
+
newConfig.url = opts.url;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Freeze to prevent accidental mutation
|
|
59
|
+
config = Object.freeze(newConfig);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
module.exports.getConfig = () => config;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
module.exports.validateLog = (log) => {
|
|
2
|
+
if (!log.event_type) throw new Error("event_type required");
|
|
3
|
+
if (!log.actor_id) throw new Error("actor_id required");
|
|
4
|
+
if (!log.actor_name) throw new Error("actor_name required");
|
|
5
|
+
if (!log.resource_id) throw new Error("resource_id required");
|
|
6
|
+
if (!log.resource_type) throw new Error("resource_type required");
|
|
7
|
+
};
|
package/index.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
const { setConfig, getConfig } = require("./core/config");
|
|
2
|
+
const { validateLog } = require("./core/validator");
|
|
3
|
+
|
|
4
|
+
const { initLocal } = require("./local/initLocal");
|
|
5
|
+
const { writeLocal } = require("./local/writeLocal");
|
|
6
|
+
|
|
7
|
+
const { sendToCloud } = require("./cloud/sendToCloud");
|
|
8
|
+
const retryQueue = require("./cloud/retryQueue");
|
|
9
|
+
const { addMetrics } = require("./cloud/addMetrics");
|
|
10
|
+
const { overrideConsoleMethods } = require("./cloud/addLogs");
|
|
11
|
+
|
|
12
|
+
let pool = null;
|
|
13
|
+
/**
|
|
14
|
+
* Initializes the Audit SDK.
|
|
15
|
+
*
|
|
16
|
+
* @typedef {Object} Config - Initialization options.
|
|
17
|
+
* @property {"local"|"cloud"} mode - Current mode.
|
|
18
|
+
* @property {string | Object} [db] - Database URL string or a database config object.
|
|
19
|
+
* @property {Object} db.client - A database client/pool instance (e.g., new Pool()).
|
|
20
|
+
* @property {string} [apiKey] - API key for cloud mode.
|
|
21
|
+
* @property {string} [secretKey] - Secret key for cloud mode.
|
|
22
|
+
* @property {string} [url] - URL for cloud mode
|
|
23
|
+
*
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Initializes the Audit SDK.
|
|
28
|
+
*
|
|
29
|
+
* @param {Config} options - Initialization options.
|
|
30
|
+
* @example
|
|
31
|
+
* // Local mode
|
|
32
|
+
* init({
|
|
33
|
+
* mode: "local",
|
|
34
|
+
* db: { client: new Pool({ connectionString: "postgres://..." }) }
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // Cloud mode
|
|
39
|
+
* init({
|
|
40
|
+
* mode: "cloud",
|
|
41
|
+
* apiKey: "my-project-api-key"
|
|
42
|
+
* });
|
|
43
|
+
*/
|
|
44
|
+
module.exports.init = async (options) => {
|
|
45
|
+
try {
|
|
46
|
+
setConfig(options);
|
|
47
|
+
|
|
48
|
+
const config = getConfig();
|
|
49
|
+
|
|
50
|
+
if (config.mode === "local") {
|
|
51
|
+
pool = await initLocal(config);
|
|
52
|
+
}
|
|
53
|
+
else{
|
|
54
|
+
overrideConsoleMethods();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log(`Audit log SDK initialized in ${config.mode} mode`);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
throw new Error(error);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @typedef {Object} AuditLog
|
|
65
|
+
* @property {string} project_id - Project ID (needed if config.mode is 'local')
|
|
66
|
+
* @property {string} event_type - Event type.
|
|
67
|
+
* @property {string} actor_id - Actor ID.
|
|
68
|
+
* @property {string} actor_name - Actor name.
|
|
69
|
+
* @property {string} resource_id - Resource ID.
|
|
70
|
+
* @property {string} resource_type - Resource type.
|
|
71
|
+
* @property {string} [user_agent] - User agent.
|
|
72
|
+
* @property {string} [ip_address] - IP address.
|
|
73
|
+
* @property {Object} metdata - Metadata.
|
|
74
|
+
*/
|
|
75
|
+
/**
|
|
76
|
+
* @param {AuditLog} log
|
|
77
|
+
*/
|
|
78
|
+
module.exports.log = async (log) => {
|
|
79
|
+
try {
|
|
80
|
+
validateLog(log);
|
|
81
|
+
|
|
82
|
+
const config = getConfig();
|
|
83
|
+
|
|
84
|
+
if (config.mode === "local") {
|
|
85
|
+
await writeLocal(pool, log);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (config.mode === "cloud") {
|
|
89
|
+
await sendToCloud(log, false);
|
|
90
|
+
}
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw new Error(error);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Flushes pending logs.
|
|
98
|
+
* Processes any logs stored in the retry queue and attempts
|
|
99
|
+
* to send them again (only in cloud mode).
|
|
100
|
+
* @returns {Object} summary - If mode is "cloud", it returns a summary of
|
|
101
|
+
* logs sent, requeued and dead
|
|
102
|
+
*/
|
|
103
|
+
module.exports.flush = async () => {
|
|
104
|
+
try {
|
|
105
|
+
const config = getConfig();
|
|
106
|
+
|
|
107
|
+
if (config.mode === "cloud") {
|
|
108
|
+
const summary = await retryQueue.flush(config);
|
|
109
|
+
return summary;
|
|
110
|
+
}
|
|
111
|
+
} catch (error) {
|
|
112
|
+
throw new Error("Could not execute flush");
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @typedef {Object} Metric
|
|
118
|
+
* @property {string} name - metric name.
|
|
119
|
+
* @property {string} value - value.
|
|
120
|
+
* @property {string} type - ["gauge", "counter", "histogram"].
|
|
121
|
+
* @property {Object} tags - tags.
|
|
122
|
+
*/
|
|
123
|
+
/**
|
|
124
|
+
* @param {Metric} metric
|
|
125
|
+
*/
|
|
126
|
+
module.exports.addMetric = async (metric) => {
|
|
127
|
+
try {
|
|
128
|
+
const config = getConfig();
|
|
129
|
+
|
|
130
|
+
if (config.mode === "cloud") {
|
|
131
|
+
await addMetrics(metric);
|
|
132
|
+
}
|
|
133
|
+
} catch (error) {
|
|
134
|
+
throw new Error("Could not add metric");
|
|
135
|
+
}
|
|
136
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
|
|
3
|
+
module.exports.initLocal = async (config) => {
|
|
4
|
+
const pool = config.db.client;
|
|
5
|
+
const migration = fs.readFileSync(__dirname + "/migration.sql", "utf8");
|
|
6
|
+
try {
|
|
7
|
+
await pool.query(migration);
|
|
8
|
+
return pool;
|
|
9
|
+
} catch (error) {
|
|
10
|
+
throw new Error("Error in initializing Audit SDK. Could not connect to database.");
|
|
11
|
+
}
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
2
|
+
id SERIAL PRIMARY KEY,
|
|
3
|
+
event_type TEXT NOT NULL,
|
|
4
|
+
actor_id TEXT NOT NULL,
|
|
5
|
+
actor_name TEXT NOT NULL,
|
|
6
|
+
resource_id TEXT NOT NULL,
|
|
7
|
+
resource_type TEXT NOT NULL,
|
|
8
|
+
ip_address TEXT,
|
|
9
|
+
user_agent TEXT,
|
|
10
|
+
metadata JSONB,
|
|
11
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
12
|
+
);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module.exports.writeLocal = async (pool, log) => {
|
|
2
|
+
const client = await pool.connect();
|
|
3
|
+
|
|
4
|
+
try {
|
|
5
|
+
await client.query("BEGIN");
|
|
6
|
+
|
|
7
|
+
await client.query(
|
|
8
|
+
`INSERT INTO events (event_type, actor_id, actor_name, resource_id, resource_type, ip_address, user_agent, metadata)
|
|
9
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
|
|
10
|
+
[
|
|
11
|
+
log.event_type,
|
|
12
|
+
log.actor_id,
|
|
13
|
+
log.actor_name,
|
|
14
|
+
log.resource_id,
|
|
15
|
+
log.resource_type,
|
|
16
|
+
log.ip_address || null,
|
|
17
|
+
log.user_agent || null,
|
|
18
|
+
log.metadata || {}
|
|
19
|
+
]
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
await client.query("COMMIT");
|
|
23
|
+
console.log("logged successfully! -", log)
|
|
24
|
+
} catch (err) {
|
|
25
|
+
await client.query("ROLLBACK");
|
|
26
|
+
throw err;
|
|
27
|
+
} finally {
|
|
28
|
+
client.release();
|
|
29
|
+
}
|
|
30
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@logmint/sdk",
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "LogMint SDK for sending audit logs, metrics, application logs",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"type": "commonjs",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"logmint",
|
|
12
|
+
"audit",
|
|
13
|
+
"logs",
|
|
14
|
+
"sdk",
|
|
15
|
+
"monitor",
|
|
16
|
+
"metrics"
|
|
17
|
+
],
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/this-is-shreya/logmint-audit-sdk.git"
|
|
21
|
+
},
|
|
22
|
+
"author": "Shreya Srivastava",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"axios": "^1.13.2"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"index.js",
|
|
29
|
+
"core",
|
|
30
|
+
"cloud",
|
|
31
|
+
"local"
|
|
32
|
+
],
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
}
|
|
36
|
+
}
|