Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/common/telemetry/bin/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ int main(int argc, char* argv[])
OsConfigLogInfo(g_log, "%s", init_message.c_str());

OsConfigLogInfo(g_log, "Telemetry initializing...");
Telemetry::TelemetryManager telemetryManager(args.verbose, args.teardown_time, g_log);
std::string cacheFilePath(Telemetry::TelemetryManager::TELEMETRY_CACHE_FILE_NAME);
Telemetry::TelemetryManager telemetryManager(cacheFilePath, args.verbose, args.teardown_time, g_log);

(void)(telemetryManager.ProcessJsonFile(args.filepath));
OsConfigLogInfo(g_log, "Processed telemetry JSON file: %s", args.filepath.c_str());
Expand Down
8 changes: 5 additions & 3 deletions src/common/telemetry/bin/tests/TelemetryBinTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@ class TelemetryBinTest : public ::testing::Test
{
protected:
std::string m_testDir;
std::string m_telemetryCache;

void SetUp() override
{
// Create a temporary directory for test files
char tmpDir[] = "/tmp/telemetry_test_XXXXXX";
ASSERT_NE(nullptr, mkdtemp(tmpDir));
m_testDir = std::string(tmpDir);

m_telemetryCache = m_testDir + "/telemetry_cache_db";
}

void TearDown() override
Expand Down Expand Up @@ -290,14 +293,13 @@ TEST_F(TelemetryBinTest, FileAndPositionalArgumentCannotBothBeUsed)
TEST_F(TelemetryBinTest, ProcessesValidSingleLineJson)
{
std::string jsonFile = CreateTestJsonFile(R"({"EventName":"TestEvent","TestKey":"TestValue"})");

try
{
Telemetry::TelemetryManager telemetryManager(false, std::chrono::seconds{1});
Telemetry::TelemetryManager telemetryManager(m_telemetryCache, false, std::chrono::seconds{1});
EXPECT_FALSE(telemetryManager.ProcessJsonFile(jsonFile));
}
catch (const std::exception& e)
{
GTEST_SKIP() << "TelemetryManager creation failed: " << e.what();
EXPECT_TRUE(false);
}
}
29 changes: 25 additions & 4 deletions src/common/telemetry/lib/Telemetry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,37 @@ using namespace MAT;
namespace Telemetry
{

TelemetryManager::TelemetryManager(bool enableDebug, std::chrono::seconds teardownTime, OsConfigLogHandle logHandle)
TelemetryManager::TelemetryManager(std::string cacheFilePath,bool enableDebug, std::chrono::seconds teardownTime, OsConfigLogHandle logHandle)
: m_log(logHandle)
, m_logManager(nullptr)
, m_logger(nullptr)
{
{
FILE* testWrite = fopen(cacheFilePath.c_str(), "a");
if (testWrite)
{
fclose(testWrite);
}
else
{
OsConfigLogError(m_log, "Telemetry sdk cache path '%s' not writable aborting", cacheFilePath.c_str() );
throw std::runtime_error("Telemetry sdk cache path not writable aborting");
}
}

m_logConfig["name"] = TELEMETRY_NAME;
m_logConfig["version"] = TELEMETRY_VERSION;
m_logConfig["config"]["host"] = "*";
m_logConfig[CFG_BOOL_ENABLE_TRACE] = enableDebug;
m_logConfig[CFG_INT_TRACE_LEVEL_MIN] = 0;
m_logConfig[CFG_INT_MAX_TEARDOWN_TIME] = teardownTime.count();
m_logConfig[CFG_STR_CACHE_FILE_PATH] = TELEMETRY_CACHE_FILE_NAME;
m_logConfig[CFG_STR_CACHE_FILE_PATH] = cacheFilePath;
m_logConfig[CFG_INT_CACHE_FILE_SIZE] = TELEMETRY_CACHE_FILE_SIZE;
m_logConfig[CFG_INT_RAM_QUEUE_SIZE] = TELEMETRY_RAM_QUEUE_SIZE;
m_logConfig[CFG_BOOL_ENABLE_DB_DROP_IF_FULL] = true;

status_t status = STATUS_SUCCESS;
m_logManager.reset(LogManagerProvider::CreateLogManager(m_logConfig, status));
m_logManager = LogManagerProvider::CreateLogManager(m_logConfig, status);
if ((STATUS_SUCCESS != status) || !m_logManager)
{
OsConfigLogError(m_log, "Telemetry initialization failed. status=%d", status);
Expand All @@ -59,12 +72,20 @@ TelemetryManager::~TelemetryManager() noexcept
{
try
{
m_logManager->FlushAndTeardown();
if (nullptr != m_logManager)
{
m_logManager->FlushAndTeardown();
}
}
catch(...)
{
OsConfigLogError(m_log, "Exception during telemetry shutdown");
}

LogManagerProvider::DestroyLogManager(TELEMETRY_NAME);

m_logger = nullptr;
m_logManager = nullptr;
OsConfigLogInfo(m_log, "Telemetry shutdown complete.");
}

Expand Down
6 changes: 3 additions & 3 deletions src/common/telemetry/lib/Telemetry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
#include <LogManager.hpp>
#include <Logging.h>
#include <chrono>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
Expand All @@ -29,7 +28,8 @@ class TelemetryManager
static constexpr const int TELEMETRY_CACHE_FILE_SIZE = 10 * 1024 * 1024;
static constexpr const int TELEMETRY_RAM_QUEUE_SIZE = 2 * 1024 * 1024;

explicit TelemetryManager(bool enableDebug = false, std::chrono::seconds teardownTime = CONFIG_DEFAULT_TEARDOWN_TIME, OsConfigLogHandle logHandle = nullptr);
explicit TelemetryManager(std::string cacheFilePath, bool enableDebug = false, std::chrono::seconds teardownTime = CONFIG_DEFAULT_TEARDOWN_TIME,
OsConfigLogHandle logHandle = nullptr);

~TelemetryManager() noexcept;

Expand All @@ -45,7 +45,7 @@ class TelemetryManager
private:
OsConfigLogHandle m_log;
MAT::ILogConfiguration m_logConfig;
std::unique_ptr<MAT::ILogManager> m_logManager;
MAT::ILogManager* m_logManager;
MAT::ILogger* m_logger;

void EventWrite(Microsoft::Applications::Events::EventProperties event);
Expand Down
41 changes: 26 additions & 15 deletions src/common/telemetry/lib/tests/TelemetryTests.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#include <cstdlib>

#include <chrono>
#include <fstream>
#include <gtest/gtest.h>
Expand All @@ -13,6 +15,7 @@ class TelemetryTest : public ::testing::Test
protected:
std::string m_testDir;
std::string m_testJsonFile;
std::string m_telemetryCache;

void SetUp() override
{
Expand All @@ -21,7 +24,15 @@ class TelemetryTest : public ::testing::Test
ASSERT_NE(nullptr, mkdtemp(tmpDir));
m_testDir = std::string(tmpDir);
m_testJsonFile = m_testDir + "/test_telemetry.json";
}

std::string telemetryCache = m_testDir + "/telemetry_cache_db_XXXXXX";
std::vector<char> buffer(telemetryCache.begin(), telemetryCache.end());
buffer.push_back('\0');

int fd = ::mkstemp(buffer.data());
EXPECT_TRUE(0 < fd);
m_telemetryCache.assign(buffer.data(), buffer.size());
}

void TearDown() override
{
Expand Down Expand Up @@ -50,15 +61,15 @@ class TelemetryTest : public ::testing::Test
// Test processing a non-existent file
TEST_F(TelemetryTest, ProcessNonExistentFile)
{
Telemetry::TelemetryManager telemetryManager(false, std::chrono::seconds(1));
Telemetry::TelemetryManager telemetryManager(m_telemetryCache, false, std::chrono::seconds(1));
EXPECT_FALSE(telemetryManager.ProcessJsonFile("/non/existent/file.json"));
}

// Test processing an empty file
TEST_F(TelemetryTest, ProcessEmptyFile)
{
ASSERT_TRUE(CreateTestJsonFile(""));
Telemetry::TelemetryManager telemetryManager(false, std::chrono::seconds(1));
Telemetry::TelemetryManager telemetryManager(m_telemetryCache, false, std::chrono::seconds(1));
EXPECT_TRUE(telemetryManager.ProcessJsonFile(m_testJsonFile)); // Empty file should be processed successfully
}

Expand All @@ -68,15 +79,15 @@ TEST_F(TelemetryTest, ProcessValidJsonFile)
// Create a test file with a valid event
std::string validJson = R"({"EventName": "TestEvent", "TestParam": "value"})";
ASSERT_TRUE(CreateTestJsonFile(validJson));
Telemetry::TelemetryManager telemetryManager(false, std::chrono::seconds(1));
Telemetry::TelemetryManager telemetryManager(m_telemetryCache, false, std::chrono::seconds(1));
EXPECT_FALSE(telemetryManager.ProcessJsonFile(m_testJsonFile));
}

// Test processing a file with invalid JSON
TEST_F(TelemetryTest, ProcessInvalidJsonFile)
{
ASSERT_TRUE(CreateTestJsonFile("invalid json content"));
Telemetry::TelemetryManager telemetryManager(false, std::chrono::seconds(1));
Telemetry::TelemetryManager telemetryManager(m_telemetryCache, false, std::chrono::seconds(1));
EXPECT_FALSE(telemetryManager.ProcessJsonFile(m_testJsonFile));
}

Expand All @@ -87,7 +98,7 @@ TEST_F(TelemetryTest, ProcessMixedJsonFile)
invalid json line
{"EventName": "AnotherEvent", "Param": "value2"})";
ASSERT_TRUE(CreateTestJsonFile(mixedContent));
Telemetry::TelemetryManager telemetryManager(false, std::chrono::seconds(1));
Telemetry::TelemetryManager telemetryManager(m_telemetryCache, false, std::chrono::seconds(1));
EXPECT_FALSE(telemetryManager.ProcessJsonFile(m_testJsonFile));
}

Expand All @@ -98,7 +109,7 @@ TEST_F(TelemetryTest, ProcessMultipleValidJsonLines)
{"EventName": "Event2", "Param2": "value2"}
{"EventName": "Event3", "Param3": "value3"})";
ASSERT_TRUE(CreateTestJsonFile(multipleLines));
Telemetry::TelemetryManager telemetryManager(false, std::chrono::seconds(1));
Telemetry::TelemetryManager telemetryManager(m_telemetryCache, false, std::chrono::seconds(1));
EXPECT_FALSE(telemetryManager.ProcessJsonFile(m_testJsonFile));
}

Expand All @@ -107,62 +118,62 @@ TEST_F(TelemetryTest, ProcessRuleCompleteEvent)
{
std::string realEvent = R"({"EventName":"RuleComplete","Timestamp":"2025-10-17 22:52:56+0000","ComponentName":"SecurityBaseline","ObjectName":"auditEnsureAuditdInstalled","ObjectResult":"0","Microseconds":"29","DistroName":"CentOS","CorrelationId":"","Version":"1.0.5.20251017-g03b36b7d"})";
ASSERT_TRUE(CreateTestJsonFile(realEvent));
Telemetry::TelemetryManager telemetryManager(false, std::chrono::seconds(1));
Telemetry::TelemetryManager telemetryManager(m_telemetryCache, false, std::chrono::seconds(1));
EXPECT_TRUE(telemetryManager.ProcessJsonFile(m_testJsonFile));
}

TEST_F(TelemetryTest, ProcessRuleCompleteMissingComponentNameEvent)
{
std::string realEvent = R"({"EventName":"RuleComplete","Timestamp":"2025-10-17 22:52:56+0000","ComponentName":"SecurityBaseline","ObjectResult":"0","Microseconds":"29","DistroName":"CentOS","CorrelationId":"","Version":"1.0.5.20251017-g03b36b7d"})";
ASSERT_TRUE(CreateTestJsonFile(realEvent));
Telemetry::TelemetryManager telemetryManager(false, std::chrono::seconds(1));
Telemetry::TelemetryManager telemetryManager(m_telemetryCache, false, std::chrono::seconds(1));
EXPECT_FALSE(telemetryManager.ProcessJsonFile(m_testJsonFile));
}

TEST_F(TelemetryTest, ProcessBaselineRunEvent)
{
std::string realEvent = R"({"EventName":"BaselineRun","Timestamp":"2025-10-17 22:52:56+0000","BaselineName":"Azure Security Baseline for Linux","Mode":"audit-only","DurationSeconds":"8.87","DistroName":"CentOS","CorrelationId":"","Version":"1.0.5.20251017-g03b36b7d"})";
ASSERT_TRUE(CreateTestJsonFile(realEvent));
Telemetry::TelemetryManager telemetryManager(false, std::chrono::seconds(1));
Telemetry::TelemetryManager telemetryManager(m_telemetryCache, false, std::chrono::seconds(1));
EXPECT_TRUE(telemetryManager.ProcessJsonFile(m_testJsonFile));
}

TEST_F(TelemetryTest, ProcessBaselineRunMissingTimestampEvent)
{
std::string realEvent = R"({"EventName":"BaselineRun","BaselineName":"Azure Security Baseline for Linux","Mode":"audit-only","DurationSeconds":"8.87","DistroName":"CentOS","CorrelationId":"","Version":"1.0.5.20251017-g03b36b7d"})";
ASSERT_TRUE(CreateTestJsonFile(realEvent));
Telemetry::TelemetryManager telemetryManager(false, std::chrono::seconds(1));
Telemetry::TelemetryManager telemetryManager(m_telemetryCache, false, std::chrono::seconds(1));
EXPECT_FALSE(telemetryManager.ProcessJsonFile(m_testJsonFile));
}

TEST_F(TelemetryTest, ProcessStatusTraceEvent)
{
std::string realEvent = R"({"EventName":"StatusTrace","Timestamp":"2025-10-17 22:52:56+0000","FileName":"/workspaces/azure-osconfig/src/common/asb/Asb.c","LineNumber":"1109","FunctionName":"AsbShutdown","RuleCodename":"auditEnsureSmbWithSambaIsDisabled","CallingFunctionName":"TestingStatusTrace","ResultCode":"0","ResultString":"OK","ScenarioName":"TestingScenario","Microseconds":"101807","DistroName":"CentOS","CorrelationId":"","Version":"1.0.5.20251017-g03b36b7d"})";
ASSERT_TRUE(CreateTestJsonFile(realEvent));
Telemetry::TelemetryManager telemetryManager(false, std::chrono::seconds(1));
Telemetry::TelemetryManager telemetryManager(m_telemetryCache, false, std::chrono::seconds(1));
EXPECT_TRUE(telemetryManager.ProcessJsonFile(m_testJsonFile));
}

TEST_F(TelemetryTest, ProcessStatusTraceMissingScenarioNameEvent)
{
std::string realEvent = R"({"EventName":"StatusTrace","Timestamp":"2025-10-17 22:52:56+0000","FileName":"/workspaces/azure-osconfig/src/common/asb/Asb.c","LineNumber":"1109","FunctionName":"AsbShutdown","RuleCodename":"auditEnsureSmbWithSambaIsDisabled","CallingFunctionName":"TestingStatusTrace","ResultCode":"0","Microseconds":"101807","DistroName":"CentOS","CorrelationId":"","Version":"1.0.5.20251017-g03b36b7d"})";
ASSERT_TRUE(CreateTestJsonFile(realEvent));
Telemetry::TelemetryManager telemetryManager(false, std::chrono::seconds(1));
Telemetry::TelemetryManager telemetryManager(m_telemetryCache, false, std::chrono::seconds(1));
EXPECT_FALSE(telemetryManager.ProcessJsonFile(m_testJsonFile));
}

TEST_F(TelemetryTest, ProcessCommandExecutedEvent)
{
std::string realEvent = R"({"EventName":"CommandExecuted","DistroName":"CentOS","Version":"1.3.0-preview123","CorrelationId":"{00000000-0000-0000-0000-000000000000}","CorrelationGroup":"TestGroup","Timestamp":"2025-10-17 22:52:56+0000","IsTestMode":true,"Subcommand":"get resource","Duration":1234.5678,"Success":true,"ErrorKind":"Resource","ErrorResourceName":"Test Resource","ErrorResourceType":"Microsoft.OSConfig/Test","ErrorLocation":"utils/test.rs:123","ErrorCode":1})";
ASSERT_TRUE(CreateTestJsonFile(realEvent));
Telemetry::TelemetryManager telemetryManager(false, std::chrono::seconds(1));
Telemetry::TelemetryManager telemetryManager(m_telemetryCache, false, std::chrono::seconds(1));
EXPECT_TRUE(telemetryManager.ProcessJsonFile(m_testJsonFile));
}

TEST_F(TelemetryTest, ProcessCommandExecutedMissingSubcommandEvent)
{
std::string realEvent = R"({"EventName":"CommandExecuted","DistroName":"CentOS","Version":"1.3.0-preview123","CorrelationId":"{00000000-0000-0000-0000-000000000000}","CorrelationGroup":"TestGroup","Timestamp":"2025-10-17 22:52:56+0000","IsTestMode":true,"Duration":1234.5678,"Success":true,"ErrorKind":"Resource","ErrorResourceName":"Test Resource","ErrorResourceType":"Microsoft.OSConfig/Test","ErrorLocation":"utils/test.rs:123","ErrorCode":1})";
ASSERT_TRUE(CreateTestJsonFile(realEvent));
Telemetry::TelemetryManager telemetryManager(false, std::chrono::seconds(1));
Telemetry::TelemetryManager telemetryManager(m_telemetryCache, false, std::chrono::seconds(1));
EXPECT_FALSE(telemetryManager.ProcessJsonFile(m_testJsonFile));
}
Loading