Skip to content

Commit 3f74636

Browse files
committed
Added SDK and docker
1 parent 55620bf commit 3f74636

File tree

3 files changed

+250
-31
lines changed

3 files changed

+250
-31
lines changed

api/jobs/ping.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,15 @@ class PingJob extends job.Job {
2121
return done();
2222
}
2323

24-
var custom = tracker.getAllData();
25-
if (Object.keys(custom).length) {
26-
user.user_details({"custom": custom });
24+
try {
25+
var custom = await tracker.getAllData();
26+
console.log("custom", custom);
27+
if (Object.keys(custom).length) {
28+
user.user_details({"custom": custom });
29+
}
30+
}
31+
catch (ex) {
32+
console.log("Error collecting server data:", ex);
2733
}
2834
var days = 90;
2935
var current_sync = Date.now();

api/parts/mgmt/tracker.js

Lines changed: 239 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,11 @@ tracker.enable = function() {
7979
Countly.track_errors();
8080
}
8181
setTimeout(function() {
82-
var custom = tracker.getAllData();
83-
if (Object.keys(custom).length) {
84-
Countly.user_details({"custom": custom });
85-
}
82+
tracker.getAllData().then((custom) => {
83+
if (Object.keys(custom).length) {
84+
Countly.user_details({"custom": custom });
85+
}
86+
});
8687
}, 20000);
8788
}
8889
};
@@ -167,30 +168,33 @@ tracker.getSDK = function() {
167168

168169
/**
169170
* Get server stats
171+
* @returns {Promise<Object>} server stats
170172
**/
171173
tracker.collectServerStats = function() {
172174
var props = {};
173-
stats.getServer(common.db, function(data) {
174-
common.db.collection("apps").aggregate([{$project: {last_data: 1}}, {$sort: {"last_data": -1}}, {$limit: 1}], {allowDiskUse: true}, function(errApps, resApps) {
175-
common.db.collection("members").aggregate([{$project: {last_login: 1}}, {$sort: {"last_login": -1}}, {$limit: 1}], {allowDiskUse: true}, function(errLogin, resLogin) {
176-
if (resApps && resApps[0]) {
177-
props.last_data = resApps[0].last_data || 0;
178-
}
179-
if (resLogin && resLogin[0]) {
180-
props.last_login = resLogin[0].last_login || 0;
181-
}
182-
if (data) {
183-
if (data.app_users) {
184-
props.app_users = data.app_users;
175+
return new Promise((resolve) => {
176+
stats.getServer(common.db, function(data) {
177+
common.db.collection("apps").aggregate([{$project: {last_data: 1}}, {$sort: {"last_data": -1}}, {$limit: 1}], {allowDiskUse: true}, function(errApps, resApps) {
178+
common.db.collection("members").aggregate([{$project: {last_login: 1}}, {$sort: {"last_login": -1}}, {$limit: 1}], {allowDiskUse: true}, function(errLogin, resLogin) {
179+
if (resApps && resApps[0]) {
180+
props.last_data = resApps[0].last_data || 0;
185181
}
186-
if (data.apps) {
187-
props.apps = data.apps;
182+
if (resLogin && resLogin[0]) {
183+
props.last_login = resLogin[0].last_login || 0;
188184
}
189-
if (data.users) {
190-
props.users = data.users;
185+
if (data) {
186+
if (data.app_users) {
187+
props.app_users = data.app_users;
188+
}
189+
if (data.apps) {
190+
props.apps = data.apps;
191+
}
192+
if (data.users) {
193+
props.users = data.users;
194+
}
191195
}
192-
}
193-
return props;
196+
resolve(props);
197+
});
194198
});
195199
});
196200
});
@@ -200,12 +204,13 @@ tracker.collectServerStats = function() {
200204
* Get server data
201205
* @returns {Object} server data
202206
**/
203-
tracker.collectServerData = function() {
207+
tracker.collectServerData = async function() {
204208
var props = {};
205209
props.trial = versionInfo.trial ? true : false;
206210
props.plugins = plugins.getPlugins();
207211
props.nodejs = process.version;
208212
props.countly = versionInfo.version;
213+
props.docker = hasDockerEnv() || hasDockerCGroup() || hasDockerMountInfo();
209214
var edition = "Lite";
210215
if (IS_FLEX) {
211216
edition = "Flex";
@@ -217,22 +222,230 @@ tracker.collectServerData = function() {
217222
if (common.db.build && common.db.build.version) {
218223
props.mongodb = common.db.build.version;
219224
}
225+
const sdkData = await tracker.getSDKData();
226+
if (sdkData && sdkData.sdk_versions && Object.keys(sdkData.sdk_versions).length) {
227+
props.sdks = Object.keys(sdkData.sdk_versions);
228+
for (const [key, value] of Object.entries(sdkData.sdk_versions)) {
229+
props[key] = value;
230+
}
231+
}
232+
220233
return props;
221234
};
222235

223236
/**
224237
* Get all eligible data
225238
* @returns {Object} all eligible data
226239
*/
227-
tracker.getAllData = function() {
240+
tracker.getAllData = async function() {
228241
var props = {};
229242
if (plugins.getConfig("tracking").server_user_details) {
230-
Object.assign(props, tracker.collectServerStats());
243+
Object.assign(props, await tracker.collectServerStats());
231244
}
232-
Object.assign(props, tracker.collectServerData());
245+
Object.assign(props, await tracker.collectServerData());
233246
return props;
234247
};
235248

249+
/**
250+
* Query sdks collection for current and previous year (month 0) and combine meta_v2 data
251+
* @returns {Promise<Object>} Combined meta_v2 data from all matching documents
252+
*/
253+
tracker.getSDKData = async function() {
254+
var currentYear = new Date().getFullYear();
255+
var previousYear = currentYear - 1;
256+
257+
// Build regex pattern to match: appid_YYYY:0_shard
258+
// Matches any app ID, year (current or previous), month 0, and any shard number
259+
var yearPattern = `(${currentYear}|${previousYear})`;
260+
var pattern = new RegExp(`^[a-f0-9]{24}_${yearPattern}:0_\\d+$`);
261+
262+
try {
263+
// Use aggregation pipeline to combine meta_v2 data on MongoDB side
264+
var pipeline = [
265+
// Match documents for current and previous year, month 0, any shard
266+
{
267+
$match: {
268+
_id: pattern
269+
}
270+
},
271+
// Project only meta_v2 field and convert to array of key-value pairs
272+
{
273+
$project: {
274+
meta_v2: { $objectToArray: "$meta_v2" }
275+
}
276+
},
277+
// Unwind meta_v2 array to process each meta key separately
278+
{
279+
$unwind: "$meta_v2"
280+
},
281+
// Convert nested objects to arrays for merging
282+
{
283+
$project: {
284+
metaKey: "$meta_v2.k",
285+
metaValue: { $objectToArray: "$meta_v2.v" }
286+
}
287+
},
288+
// Unwind nested values
289+
{
290+
$unwind: "$metaValue"
291+
},
292+
// Group by meta key and inner key to collect all unique combinations
293+
{
294+
$group: {
295+
_id: {
296+
metaKey: "$metaKey",
297+
innerKey: "$metaValue.k"
298+
},
299+
value: { $first: "$metaValue.v" }
300+
}
301+
},
302+
// Group by meta key to rebuild nested structure
303+
{
304+
$group: {
305+
_id: "$_id.metaKey",
306+
values: {
307+
$push: {
308+
k: "$_id.innerKey",
309+
v: "$value"
310+
}
311+
}
312+
}
313+
},
314+
// Convert arrays back to objects
315+
{
316+
$project: {
317+
_id: 0,
318+
k: "$_id",
319+
v: { $arrayToObject: "$values" }
320+
}
321+
},
322+
// Group all into single document
323+
{
324+
$group: {
325+
_id: null,
326+
meta_v2: {
327+
$push: {
328+
k: "$k",
329+
v: "$v"
330+
}
331+
}
332+
}
333+
},
334+
// Convert final array to object
335+
{
336+
$project: {
337+
_id: 0,
338+
meta_v2: { $arrayToObject: "$meta_v2" }
339+
}
340+
}
341+
];
342+
343+
var result = await common.db.collection("sdks").aggregate(pipeline).toArray();
344+
345+
// Extract combined meta_v2 or return empty object if no results
346+
var combinedMeta = (result && result[0] && result[0].meta_v2) ? result[0].meta_v2 : {};
347+
348+
// Process sdk_version to extract highest version per SDK
349+
var sdkVersions = {};
350+
if (combinedMeta.sdk_version) {
351+
for (var versionKey in combinedMeta.sdk_version) {
352+
// Parse SDK version format: [sdk_name]_major:minor:patch
353+
var match = versionKey.match(/^\[([^\]]+)\]_(\d+):(\d+):(\d+)$/);
354+
if (match) {
355+
var sdkName = match[1];
356+
var major = parseInt(match[2], 10);
357+
var minor = parseInt(match[3], 10);
358+
var patch = parseInt(match[4], 10);
359+
360+
// Check if this SDK exists and compare versions
361+
if (!sdkVersions[sdkName]) {
362+
sdkVersions[sdkName] = {
363+
version: `${major}.${minor}.${patch}`,
364+
major: major,
365+
minor: minor,
366+
patch: patch
367+
};
368+
}
369+
else {
370+
var current = sdkVersions[sdkName];
371+
// Compare versions (major.minor.patch)
372+
if (major > current.major ||
373+
(major === current.major && minor > current.minor) ||
374+
(major === current.major && minor === current.minor && patch > current.patch)) {
375+
sdkVersions[sdkName] = {
376+
version: `${major}.${minor}.${patch}`,
377+
major: major,
378+
minor: minor,
379+
patch: patch
380+
};
381+
}
382+
}
383+
}
384+
}
385+
}
386+
387+
// Convert to simple object with just SDK name -> version string
388+
var simpleSdkVersions = {};
389+
for (var sdk in sdkVersions) {
390+
simpleSdkVersions[`sdk_${sdk}`] = sdkVersions[sdk].version;
391+
}
392+
393+
return {
394+
meta_v2: combinedMeta,
395+
sdk_versions: simpleSdkVersions,
396+
years: [previousYear, currentYear],
397+
month: 0
398+
};
399+
}
400+
catch (error) {
401+
logger("tracker:server").error("Error querying SDK data:", error);
402+
return {
403+
meta_v2: {},
404+
error: error.message
405+
};
406+
}
407+
};
408+
409+
/**
410+
* Check if running in Docker environment
411+
* @returns {boolean} if running in docker
412+
*/
413+
function hasDockerEnv() {
414+
try {
415+
fs.statSync('/.dockerenv');
416+
return true;
417+
}
418+
catch {
419+
return false;
420+
}
421+
}
422+
423+
/**
424+
* Check if running in Docker by inspecting cgroup info
425+
* @returns {boolean} if running in docker
426+
*/
427+
function hasDockerCGroup() {
428+
try {
429+
return fs.readFileSync('/proc/self/cgroup', 'utf8').includes('docker');
430+
}
431+
catch {
432+
return false;
433+
}
434+
}
435+
436+
/**
437+
* Check if running in Docker by inspecting mountinfo
438+
* @returns {boolean} if running in docker
439+
*/
440+
function hasDockerMountInfo() {
441+
try {
442+
return fs.readFileSync('/proc/self/mountinfo', 'utf8').includes('/docker/containers/');
443+
}
444+
catch {
445+
return false;
446+
}
447+
}
448+
236449
/**
237450
* Strip traling slashes from url
238451
* @param {string} str - url to strip

plugins/server-stats/tests/job.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ describe('Stats job', () => {
4040
console.log('All Data:', allData);
4141
console.log(monthlyData);
4242

43-
should(monthlyData['Last 6 months avg']).equal(1000);
44-
should(monthlyData['Last 12 months avg']).equal(1000);
43+
should(monthlyData['DPAvg6months']).equal(1000);
44+
should(monthlyData['DPAvg12months']).equal(1000);
4545

4646
const expectedDP = [];
4747
for (let count = 0; count < 12; count += 1) {

0 commit comments

Comments
 (0)